diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index ea69e88..2e1c18a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,8 +6,8 @@ env: TEST_STATS_DELAY: 5000 jobs: - golangci: - name: lint + lint: + name: Lint runs-on: ubuntu-latest steps: @@ -49,4 +49,4 @@ jobs: run: OMQ_RABBITMQCTL=DOCKER:${{job.services.rabbitmq.id}} bin/ci/before_build.sh - name: Run go test - run: go test -count=1 -p 1 -v ./... + run: go run github.com/onsi/ginkgo/v2/ginkgo -r --randomize-all --randomize-suites --fail-on-pending --fail-on-empty --keep-going --race --trace diff --git a/cmd/cmd_suite_test.go b/cmd/cmd_suite_test.go new file mode 100644 index 0000000..3b54986 --- /dev/null +++ b/cmd/cmd_suite_test.go @@ -0,0 +1,13 @@ +package cmd_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestCmd(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "CMD Suite") +} diff --git a/cmd/cmd_test.go b/cmd/cmd_test.go index e2c34b2..f7f0864 100644 --- a/cmd/cmd_test.go +++ b/cmd/cmd_test.go @@ -1,458 +1,32 @@ package cmd import ( - "context" - "fmt" - "slices" - "strings" - "sync" - "testing" - "time" - - rabbithole "github.com/michaelklishin/rabbit-hole/v2" - "github.com/rabbitmq/omq/pkg/metrics" - "github.com/rabbitmq/omq/pkg/utils" - - "github.com/stretchr/testify/assert" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" ) -func TestPublishConsume(t *testing.T) { - type test struct { - publishProto string - publishToPrefix string - consumeProto string - consumeFromPrefix string - msgPriority string // expected/default message priority - } - - tests := []test{ - { - publishProto: "amqp", - publishToPrefix: "/queues/", - consumeProto: "amqp", - consumeFromPrefix: "/queues/", - msgPriority: "0", // https://github.com/Azure/go-amqp/issues/313 - }, - { - publishProto: "stomp", - publishToPrefix: "/topic/", - consumeProto: "amqp", - consumeFromPrefix: "/queues/", - msgPriority: "4", - }, - { - publishProto: "mqtt", - publishToPrefix: "/topic/", - consumeProto: "amqp", - consumeFromPrefix: "/queues/", - msgPriority: "4", - }, - { - publishProto: "amqp", - publishToPrefix: "/exchanges/amq.topic/", - consumeProto: "stomp", - consumeFromPrefix: "/topic/", - msgPriority: "0", // https://github.com/Azure/go-amqp/issues/313 - }, - { - publishProto: "amqp", - publishToPrefix: "/exchanges/amq.topic/", - consumeProto: "mqtt", - consumeFromPrefix: "/topic/", - msgPriority: "", - }, - { - publishProto: "stomp", - publishToPrefix: "/topic/", - consumeProto: "stomp", - consumeFromPrefix: "/topic/", - msgPriority: "", - }, - { - publishProto: "stomp", - publishToPrefix: "/topic/", - consumeProto: "mqtt", - consumeFromPrefix: "/topic/", - msgPriority: "", - }, - { - publishProto: "mqtt", - publishToPrefix: "/topic/", - consumeProto: "mqtt", - consumeFromPrefix: "/topic/", - msgPriority: "", - }, - { - publishProto: "mqtt", - publishToPrefix: "/topic/", - consumeProto: "stomp", - consumeFromPrefix: "/topic/", - msgPriority: "", - }, - } - - defer metrics.Reset() - - for _, tc := range tests { - t.Run(tc.publishProto+"-"+tc.consumeProto, func(t *testing.T) { - rootCmd := RootCmd() - - publishTo := tc.publishToPrefix + tc.publishProto + tc.consumeProto - consumeFrom := tc.consumeFromPrefix + tc.publishProto + tc.consumeProto - args := []string{tc.publishProto + "-" + tc.consumeProto, - "-C", "1", - "-D", "1", - "-t", publishTo, - "-T", consumeFrom, - "--queue-durability", "none", - "--time", "3s", // don't want to long in case of issues - } - if tc.consumeProto == "amqp" { - args = append(args, "--queues", "classic", "--cleanup-queues=true") - } - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - - err := rootCmd.Execute() - assert.Nil(t, err) - - assert.Eventually(t, func() bool { - return 1 == metrics.MessagesPublished.Get() - - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(t, func() bool { - return 1 == metrics.MessagesConsumedNormalPriority.Get() - }, 2*time.Second, 100*time.Millisecond) - metrics.Reset() - }) - } -} - -func TestPublishWithPriorities(t *testing.T) { - type test struct { - publishProto string - publishToPrefix string - consumeProto string - consumeFromPrefix string - } - - tests := []test{ - // mqtt has no concept of message priority - {publishProto: "stomp", publishToPrefix: "/topic/", consumeProto: "stomp", consumeFromPrefix: "/topic/"}, - {publishProto: "stomp", publishToPrefix: "/topic/", consumeProto: "amqp", consumeFromPrefix: "/queues/"}, - {publishProto: "amqp", publishToPrefix: "/queues/", consumeProto: "amqp", consumeFromPrefix: "/queues/"}, - {publishProto: "amqp", publishToPrefix: "/exchanges/amq.topic/", consumeProto: "stomp", consumeFromPrefix: "/topic/"}, - } - - defer metrics.Reset() - - for _, tc := range tests { - t.Run(tc.publishProto+"-"+tc.consumeProto, func(t *testing.T) { - rootCmd := RootCmd() - - publishTo := tc.publishToPrefix + tc.publishProto + tc.consumeProto - consumeFrom := tc.consumeFromPrefix + tc.publishProto + tc.consumeProto - args := []string{ - tc.publishProto + "-" + tc.consumeProto, - "-C", "1", - "-D", "1", - "-t", publishTo, - "-T", consumeFrom, - "--message-priority", "13", - "--queue-durability", "none", - "--time", "3s"} - if tc.consumeProto == "amqp" { - args = append(args, "--queues", "classic", "--cleanup-queues=true") - } - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - - err := rootCmd.Execute() - assert.Nil(t, err) - - assert.Eventually(t, func() bool { - return 1 == metrics.MessagesPublished.Get() - - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(t, func() bool { - return 1 == metrics.MessagesConsumedHighPriority.Get() - }, 2*time.Second, 100*time.Millisecond) - metrics.Reset() - }) - } -} - -func TestAMQPStreamAppPropertyFilters(t *testing.T) { - defer metrics.Reset() - - rootCmd := RootCmd() - - args := []string{"amqp", - "-C", "6", - "--publish-to", "/queues/stream-with-filters", - "--consume-from", "/queues/stream-with-filters", - "--amqp-app-property", "key1=foo,bar,baz", - "--amqp-app-property-filter", "key1=$p:ba", - "--queues", "stream", - "--cleanup-queues=true", - "--time", "2s", - } - - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - err := rootCmd.Execute() - assert.Nil(t, err) - - assert.Eventually(t, func() bool { - return 6 == metrics.MessagesPublished.Get() - - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(t, func() bool { - return 4 == metrics.MessagesConsumedNormalPriority.Get() - }, 2*time.Second, 100*time.Millisecond) -} - -func TestAMQPStreamPropertyFilters(t *testing.T) { - defer metrics.Reset() - - rootCmd := RootCmd() - - args := []string{"amqp", - "-C", "3", - "--publish-to", "/queues/stream-with-property-filters", - "--consume-from", "/queues/stream-with-property-filters", - "--amqp-subject", "foo,bar,baz", - "--amqp-property-filter", "subject=baz", - "--queues", "stream", - "--cleanup-queues=true", - "--time", "2s", - } - - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - err := rootCmd.Execute() - assert.Nil(t, err) - - assert.Eventually(t, func() bool { - return 3 == metrics.MessagesPublished.Get() - - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(t, func() bool { - return 1 == metrics.MessagesConsumedNormalPriority.Get() - }, 2*time.Second, 100*time.Millisecond) -} - -func TestFanInFromMQTTtoAMQP(t *testing.T) { - defer metrics.Reset() - - rootCmd := RootCmd() - - args := []string{"mqtt-amqp", - "--publishers", "3", - "--consumers", "1", - "-C", "5", - "--publish-to", "sensor/%d", - "--consume-from", "/queues/sensors", - "--amqp-binding-key", "sensor.#", - "--queues", "classic", - "--cleanup-queues=true", - "--time", "5s", - } - - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - err := rootCmd.Execute() - assert.Nil(t, err) - - assert.Eventually(t, func() bool { - return 15 == metrics.MessagesPublished.Get() - - }, 2*time.Second, 100*time.Millisecond) - assert.Eventually(t, func() bool { - return 15 == metrics.MessagesConsumedNormalPriority.Get() - }, 2*time.Second, 100*time.Millisecond) -} - -func TestConsumerStartupDelay(t *testing.T) { - defer metrics.Reset() - - rootCmd := RootCmd() - - args := []string{"amqp", - "-z", "10s", - "-r", "1", - "-D", "1", - "-t", "/queues/consumer-startup-delay", - "-T", "/queues/consumer-startup-delay", - "--queues", "classic", - "--cleanup-queues=true", - "--consumer-startup-delay", "3s"} - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - - var wg sync.WaitGroup - go func() { - defer wg.Done() - wg.Add(1) - err := rootCmd.Execute() - assert.Nil(t, err) - }() - - time.Sleep(2 * time.Second) - assert.Equal(t, uint64(0), metrics.MessagesConsumedNormalPriority.Get()) - - assert.Eventually(t, func() bool { - return 0 < metrics.MessagesConsumedNormalPriority.Get() - }, 10*time.Second, 100*time.Millisecond) - - wg.Wait() -} - -func TestAMQPMaxInFlight(t *testing.T) { - defer metrics.Reset() - - publishWithMaxInFlight := func(maxInFlight string) error { - +var _ = Describe("end-to-end latency uses nanoseconds only if we have both publishers and consumers", func() { + It("should use nanoseconds when no -x / -y flags are used", func() { + args := []string{"amqp", "-C", "0", "-D", "0", "-t", "/topic/foobar", "-T", "/topic/foobar"} rootCmd := RootCmd() - - args := []string{"amqp", - "-z", "3s", - "-t", "/queues/amqp-max-in-flight", - "-T", "/queues/amqp-max-in-flight", - "--queues", "stream", - "--cleanup-queues=true", - "--max-in-flight", maxInFlight} - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - return rootCmd.Execute() - } - - err := publishWithMaxInFlight("1") - assert.Nil(t, err) - publishedWithMaxInFlight1 := metrics.MessagesPublished.Get() - - metrics.Reset() - - err = publishWithMaxInFlight("8") - assert.Nil(t, err) - publishedWithMaxInFlight8 := metrics.MessagesPublished.Get() - - assert.Greater(t, publishedWithMaxInFlight8, publishedWithMaxInFlight1*2) -} - -func TestMQTTVersion(t *testing.T) { - type test struct { - versionFlag string - connectionVersion string - } + _ = rootCmd.Execute() + Expect(cfg.UseMillis).To(BeFalse()) + }) - tests := []test{ - { - versionFlag: "3", - connectionVersion: "MQTT 3-1", - }, - { - versionFlag: "4", - connectionVersion: "MQTT 3-1-1", - }, - { - versionFlag: "5", - connectionVersion: "MQTT 5-0", - }, - } - defer metrics.Reset() - - rmqc, err := rabbithole.NewClient("http://127.0.0.1:15672", "guest", "guest") - if err != nil { - t.Fatalf("Error creating RabbitMQ client: %v", err) - } - - for _, tc := range tests { - t.Run(tc.connectionVersion, func(t *testing.T) { - rootCmd := RootCmd() - - args := []string{"mqtt", - "-z", "7s", - "-r", "1"} - if tc.versionFlag != "" { - args = append(args, "--mqtt-publisher-version", tc.versionFlag) - args = append(args, "--mqtt-consumer-version", tc.versionFlag) - } - rootCmd.SetArgs(args) - fmt.Println("Running test: omq", strings.Join(args, " ")) - - var wg sync.WaitGroup - wg.Add(1) - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - go func() { - defer wg.Done() - err := rootCmd.ExecuteContext(ctx) - assert.Nil(t, err) - }() - - assert.Eventually(t, func() bool { - conns, err := rmqc.ListConnections() - return err == nil && - len(conns) >= 2 && - slices.ContainsFunc(conns, func(conn rabbithole.ConnectionInfo) bool { - return conn.Protocol == tc.connectionVersion - }) - }, 7*time.Second, 500*time.Millisecond) - - cancel() - wg.Wait() - time.Sleep(3 * time.Second) - }) - } - -} - -func TestLatencyCalculationA(t *testing.T) { - tests := []struct { - name string - useMillis bool - }{ - {"nanoseconds", false}, - {"milliseconds", true}, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - testMsg := utils.MessageBody(100) - utils.UpdatePayload(tc.useMillis, &testMsg) - time.Sleep(10 * time.Millisecond) - _, latency := utils.CalculateEndToEndLatency(&testMsg) - // not very precise but we just care about the order of magnitude - assert.Greater(t, latency.Milliseconds(), int64(9)) - assert.Less(t, latency.Milliseconds(), int64(50)) - }) - } -} - -func TestAutoUseMillis(t *testing.T) { - defer metrics.Reset() - - // by default, use-millis is false - args := []string{"amqp", "-C", "1", "-D", "1", "--queues", "classic", "--time", "2s"} - rootCmd := RootCmd() - rootCmd.SetArgs(args) - _ = rootCmd.Execute() - assert.Equal(t, false, cfg.UseMillis) - - // if -x 0, use-millis is true - args = []string{"amqp", "-x", "0", "-D", "0", "--queues", "classic", "--time", "2s"} - rootCmd = RootCmd() - rootCmd.SetArgs(args) - _ = rootCmd.Execute() - assert.Equal(t, true, cfg.UseMillis) - - // if -y 0, use-millis is true - args = []string{"amqp", "-t", "/exchanges/amq.topic/foobar", "-y", "0", "-C", "0", "--queues", "classic", "--time", "2s"} - rootCmd = RootCmd() - rootCmd.SetArgs(args) - _ = rootCmd.Execute() - assert.Equal(t, true, cfg.UseMillis) + It("should use millseconds when there are no publishers", func() { + args := []string{"amqp", "-x", "0", "-D", "0", "-T", "/topic/foobar"} + rootCmd := RootCmd() + rootCmd.SetArgs(args) + _ = rootCmd.Execute() + Expect(cfg.UseMillis).To(BeTrue()) + }) -} + It("should use millseconds when there are no consumers", func() { + args := []string{"amqp", "-y", "0", "-C", "0", "-t", "/topic/foobar"} + rootCmd := RootCmd() + rootCmd.SetArgs(args) + _ = rootCmd.Execute() + Expect(cfg.UseMillis).To(BeTrue()) + }) +}) diff --git a/cmd/root.go b/cmd/root.go index c4d3d07..85bdca8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -183,10 +183,16 @@ func RootCmd() *cobra.Command { }, PersistentPostRun: func(cmd *cobra.Command, args []string) { mgmt.DeleteDeclaredQueues() + metrics.GetMetricsServer().PrintSummary() + if cfg.PrintAllMetrics { + metrics.GetMetricsServer().PrintAll() + } }, } rootCmd.PersistentFlags(). VarP(enumflag.New(&log.Level, "log-level", log.Levels, enumflag.EnumCaseInsensitive), "log-level", "l", "Log level (debug, info, error)") + rootCmd.PersistentFlags(). + BoolVar(&cfg.PrintAllMetrics, "print-all-metrics", false, "Print all metrics before exiting") rootCmd.PersistentFlags().StringSliceVarP(&cfg.Uri, "uri", "", nil, "URI for both publishers and consumers") rootCmd.PersistentFlags().StringSliceVarP(&cfg.PublisherUri, "publisher-uri", "", nil, "URI for publishing") rootCmd.PersistentFlags().StringSliceVarP(&cfg.ConsumerUri, "consumer-uri", "", nil, "URI for consuming") @@ -275,7 +281,7 @@ func start(cfg config.Config) { cancel() println("Received SIGTERM, shutting down...") time.Sleep(500 * time.Millisecond) - shutdown(cfg.CleanupQueues) + shutdown(cfg.CleanupQueues, cfg.PrintAllMetrics) os.Exit(0) case <-ctx.Done(): return @@ -380,13 +386,17 @@ func defaultUri(proto string) string { return uri } -func shutdown(deleteQueues bool) { +func shutdown(deleteQueues bool, printAllMetrics bool) { if deleteQueues { mgmt.DeleteDeclaredQueues() } mgmt.Disconnect() metricsServer := metrics.GetMetricsServer() - metricsServer.PrintFinalMetrics() + metricsServer.PrintSummary() + + if printAllMetrics { + metricsServer.PrintAll() + } } func sanitizeConfig(cfg *config.Config) error { diff --git a/go.mod b/go.mod index f23ea55..68acb6e 100644 --- a/go.mod +++ b/go.mod @@ -11,9 +11,10 @@ require ( github.com/eclipse/paho.golang v0.21.0 github.com/eclipse/paho.mqtt.golang v1.5.0 github.com/felixge/fgprof v0.9.5 + github.com/onsi/ginkgo/v2 v2.20.2 + github.com/onsi/gomega v1.34.2 github.com/relvacode/iso8601 v1.5.0 github.com/spf13/cobra v1.8.1 - github.com/stretchr/testify v1.9.0 github.com/thediveo/enumflag/v2 v2.0.5 ) @@ -21,8 +22,10 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/lipgloss v1.0.0 // indirect github.com/charmbracelet/x/ansi v0.4.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect @@ -30,9 +33,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect - github.com/michaelklishin/rabbit-hole/v2 v2.16.0 // indirect github.com/muesli/termenv v0.15.2 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/valyala/fastrand v1.1.0 // indirect @@ -41,6 +42,8 @@ require ( golang.org/x/net v0.30.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.19.0 // indirect + golang.org/x/tools v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index dd959e4..756cd7f 100644 --- a/go.sum +++ b/go.sum @@ -11,11 +11,8 @@ github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoC github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -30,20 +27,12 @@ github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY= github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-stomp/stomp/v3 v3.1.3 h1:5/wi+bI38O1Qkf2cc7Gjlw7N5beHMWB/BxpX+4p/MGI= github.com/go-stomp/stomp/v3 v3.1.3/go.mod h1:ztzZej6T2W4Y6FlD+Tb5n7HQP3/O5UNQiuC169pIp10= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= @@ -51,25 +40,8 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134 h1:c5FlPPgxOn7kJz3VoPLkQYQXGBS3EklQ4Zfi57uOuqQ= github.com/google/pprof v0.0.0-20240910150728-a0b0bb1d4134/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= @@ -77,13 +49,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= @@ -99,62 +68,18 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/michaelklishin/rabbit-hole/v2 v2.16.0 h1:RvTPW3DnmyR7R1XTbET0ILRneU1Ou3vzIVj4YHBIE/g= -github.com/michaelklishin/rabbit-hole/v2 v2.16.0/go.mod h1:wnHAhXYVncuXKdF/3mdywRE4vzBgn4k07Z+HjdNGMpM= github.com/mkuratczyk/go-amqp v0.0.0-20241014095613-d9376f5f2d9f h1:WVdm43rq1NhTOuRrxn73Tw4j9rrrC/ilOEKWTYxFWqo= github.com/mkuratczyk/go-amqp v0.0.0-20241014095613-d9376f5f2d9f/go.mod h1:vZAogwdrkbyK3Mla8m/CxSc/aKdnTZ4IbPxl51Y5WZE= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/ginkgo/v2 v2.13.2/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4= github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc= github.com/rabbitmq/rabbitmq-amqp-go-client v0.0.0-20241001222512-5fc29f4968d0 h1:aX62gNFKXqBsacs3ejwl21dfyP8GT8WUPX04cRuLzwQ= github.com/rabbitmq/rabbitmq-amqp-go-client v0.0.0-20241001222512-5fc29f4968d0/go.mod h1:Km231GyOZAw9I3SZIqkfB9VVzCsu8jvFWYdghmnwueM= github.com/relvacode/iso8601 v1.5.0 h1:hM+cirGvOz6gKuUEqimr5TH3tiQiVOuc2QIO+nI5fY4= @@ -174,8 +99,6 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= @@ -188,177 +111,50 @@ github.com/valyala/fastrand v1.1.0 h1:f+5HkLW4rsgzdNoleUOB69hyT9IlD2ZQh9GyDMfb5G github.com/valyala/fastrand v1.1.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ= github.com/valyala/histogram v1.2.0 h1:wyYGAZZt3CpwUiIb9AU/Zbllg1llXyrtApRS815OLoQ= github.com/valyala/histogram v1.2.0/go.mod h1:Hb4kBwb4UxsaNbbbh+RRz8ZR6pdodR57tzWUS3BUzXY= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index 064e86c..964c1b8 100644 --- a/main.go +++ b/main.go @@ -9,7 +9,6 @@ import ( "github.com/rabbitmq/omq/cmd" "github.com/rabbitmq/omq/pkg/log" - "github.com/rabbitmq/omq/pkg/metrics" ) func main() { @@ -28,8 +27,6 @@ func main() { }() } - defer metrics.GetMetricsServer().PrintFinalMetrics() - cmd.Execute() if os.Getenv("OMQ_PPROF") == "true" { diff --git a/main_test.go b/main_test.go new file mode 100644 index 0000000..0cf6d20 --- /dev/null +++ b/main_test.go @@ -0,0 +1,345 @@ +package main_test + +import ( + "context" + "os/exec" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gbytes" + "github.com/onsi/gomega/gexec" +) + +var _ = Describe("OMQ Tests", func() { + DescribeTable("should support any combination of protocols", + func(publishProto string, publishToPrefix string, consumeProto string, consumeFromPrefix string) { + publishTo := publishToPrefix + publishProto + consumeProto + consumeFrom := consumeFromPrefix + publishProto + consumeProto + args := []string{publishProto + "-" + consumeProto, + "-C", "1", + "-D", "1", + "-t", publishTo, + "-T", consumeFrom, + "--queue-durability", "none", + "--time", "3s", // don't want to long in case of issues + "--print-all-metrics", + } + if consumeProto == "amqp" { + args = append(args, "--queues", "classic", "--cleanup-queues=true") + } + ctx := context.Background() + cmd := exec.CommandContext(ctx, omq, args...) + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).WithTimeout(4 * time.Second).Should(gexec.Exit(0)) + + Eventually(session.Err).Should(gbytes.Say(`TOTAL PUBLISHED messages=1`)) + Eventually(session.Err).Should(gbytes.Say(`TOTAL CONSUMED messages=1`)) + Eventually(session).Should(gbytes.Say(`omq_messages_consumed_total{priority="normal"} 1`)) + }, + Entry("amqp -> amqp", "amqp", "/queues/", "amqp", "/queues/"), // https://github.com/Azure/go-amqp/issues/313 + Entry("stomp -> amqp", "stomp", "/topic/", "amqp", "/queues/"), + Entry("mqtt -> amqp", "mqtt", "/topic/", "amqp", "/queues/"), + Entry("amqp -> stomp", "amqp", "/exchanges/amq.topic/", "stomp", "/topic/"), + Entry("amqp -> mqtt", "amqp", "/exchanges/amq.topic/", "mqtt", "/topic/"), + Entry("stomp -> stomp", "stomp", "/topic/", "stomp", "/topic/"), + Entry("stomp -> mqtt", "stomp", "/topic/", "mqtt", "/topic/"), + Entry("mqtt -> mqtt", "mqtt", "/topic/", "mqtt", "/topic/"), + Entry("mqtt -> stomp", "mqtt", "/topic/", "stomp", "/topic/"), + ) + + DescribeTable("AMQP and STOMP support message priorities", + func(publishProto string, publishToPrefix string, consumeProto string, consumeFromPrefix string) { + publishTo := publishToPrefix + publishProto + consumeProto + consumeFrom := consumeFromPrefix + publishProto + consumeProto + args := []string{publishProto + "-" + consumeProto, + "-C", "1", + "-D", "1", + "-t", publishTo, + "-T", consumeFrom, + "--message-priority", "13", + "--queue-durability", "none", + "--time", "3s", // don't want to long in case of issues + "--print-all-metrics", + } + if consumeProto == "amqp" { + args = append(args, "--queues", "classic", "--cleanup-queues=true") + } + ctx := context.Background() + cmd := exec.CommandContext(ctx, omq, args...) + session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(session).WithTimeout(4 * time.Second).Should(gexec.Exit(0)) + + Eventually(session.Err).Should(gbytes.Say(`TOTAL PUBLISHED messages=1`)) + Eventually(session.Err).Should(gbytes.Say(`TOTAL CONSUMED messages=1`)) + Eventually(session).Should(gbytes.Say(`omq_messages_consumed_total{priority="high"} 1`)) + }, + Entry("amqp -> amqp", "amqp", "/queues/", "amqp", "/queues/"), + Entry("stomp -> amqp", "stomp", "/topic/", "amqp", "/queues/"), + Entry("amqp -> stomp", "amqp", "/exchanges/amq.topic/", "stomp", "/topic/"), + Entry("stomp -> stomp", "stomp", "/topic/", "stomp", "/topic/"), + ) + + // Describe("AMQP Stream App Property Filters", func() { + // It("should filter messages based on app properties", func() { + // rootCmd := RootCmd() + + // args := []string{"amqp", + // "-C", "6", + // "--publish-to", "/queues/stream-with-filters", + // "--consume-from", "/queues/stream-with-filters", + // "--amqp-app-property", "key1=foo,bar,baz", + // "--amqp-app-property-filter", "key1=$p:ba", + // "--queues", "stream", + // "--cleanup-queues=true", + // "--time", "2s", + // } + + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // Expect(err).ShouldNot(HaveOccurred()) + // Eventually(session).Should(gexec.Exit(0)) + + // Eventually(func() int { + // return int(metrics.MessagesPublished.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(6)) + // Eventually(func() int { + // return int(metrics.MessagesConsumedNormalPriority.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(4)) + // }) + // }) + + // Describe("AMQP Stream Property Filters", func() { + // It("should filter messages based on properties", func() { + // rootCmd := RootCmd() + + // args := []string{"amqp", + // "-C", "3", + // "--publish-to", "/queues/stream-with-property-filters", + // "--consume-from", "/queues/stream-with-property-filters", + // "--amqp-subject", "foo,bar,baz", + // "--amqp-property-filter", "subject=baz", + // "--queues", "stream", + // "--cleanup-queues=true", + // "--time", "2s", + // } + + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // Expect(err).ShouldNot(HaveOccurred()) + // Eventually(session).Should(gexec.Exit(0)) + + // Eventually(func() int { + // return int(metrics.MessagesPublished.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(3)) + // Eventually(func() int { + // return int(metrics.MessagesConsumedNormalPriority.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(1)) + // }) + // }) + + // Describe("Fan-In from MQTT to AMQP", func() { + // It("should fan-in messages from MQTT to AMQP", func() { + // rootCmd := RootCmd() + + // args := []string{"mqtt-amqp", + // "--publishers", "3", + // "--consumers", "1", + // "-C", "5", + // "--publish-to", "sensor/%d", + // "--consume-from", "/queues/sensors", + // "--amqp-binding-key", "sensor.#", + // "--queues", "classic", + // "--cleanup-queues=true", + // "--time", "5s", + // } + + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // Expect(err).ShouldNot(HaveOccurred()) + // Eventually(session).Should(gexec.Exit(0)) + + // Eventually(func() int { + // return int(metrics.MessagesPublished.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(15)) + // Eventually(func() int { + // return int(metrics.MessagesConsumedNormalPriority.Get()) + // }, 2*time.Second, 100*time.Millisecond).Should(Equal(15)) + // }) + // }) + + // Describe("Consumer Startup Delay", func() { + // It("should handle consumer startup delay", func() { + // rootCmd := RootCmd() + + // args := []string{"amqp", + // "-z", "10s", + // "-r", "1", + // "-D", "1", + // "-t", "/queues/consumer-startup-delay", + // "-T", "/queues/consumer-startup-delay", + // "--queues", "classic", + // "--cleanup-queues=true", + // "--consumer-startup-delay", "3s"} + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + + // var wg sync.WaitGroup + // wg.Add(1) + // go func() { + // defer wg.Done() + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // Expect(err).ShouldNot(HaveOccurred()) + // Eventually(session).Should(gexec.Exit(0)) + // }() + + // time.Sleep(2 * time.Second) + // Expect(metrics.MessagesConsumedNormalPriority.Get()).To(Equal(uint64(0))) + + // Eventually(func() uint64 { + // return metrics.MessagesConsumedNormalPriority.Get() + // }, 10*time.Second, 100*time.Millisecond).Should(BeNumerically(">", 0)) + + // wg.Wait() + // }) + // }) + + // Describe("AMQP Max In-Flight", func() { + // It("should handle max in-flight messages", func() { + // publishWithMaxInFlight := func(maxInFlight string) error { + // rootCmd := RootCmd() + + // args := []string{"amqp", + // "-z", "3s", + // "-t", "/queues/amqp-max-in-flight", + // "-T", "/queues/amqp-max-in-flight", + // "--queues", "stream", + // "--cleanup-queues=true", + // "--max-in-flight", maxInFlight} + + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // if err != nil { + // return err + // } + // Eventually(session).Should(gexec.Exit(0)) + // return nil + // } + + // err := publishWithMaxInFlight("1") + // Expect(err).ShouldNot(HaveOccurred()) + // publishedWithMaxInFlight1 := metrics.MessagesPublished.Get() + + // metrics.Reset() + + // err = publishWithMaxInFlight("8") + // Expect(err).ShouldNot(HaveOccurred()) + // publishedWithMaxInFlight8 := metrics.MessagesPublished.Get() + + // Expect(publishedWithMaxInFlight8).Should(BeNumerically(">", publishedWithMaxInFlight1*2)) + // }) + // }) + + // Describe("MQTT Version", func() { + // type test struct { + // versionFlag string + // connectionVersion string + // } + + // tests := []test{ + // { + // versionFlag: "3", + // connectionVersion: "MQTT 3-1", + // }, + // { + // versionFlag: "4", + // connectionVersion: "MQTT 3-1-1", + // }, + // { + // versionFlag: "5", + // connectionVersion: "MQTT 5-0", + // }, + // } + + // var rmqc *rabbithole.Client + // var err error + + // BeforeEach(func() { + // rmqc, err = rabbithole.NewClient("http://127.0.0.1:15672", "guest", "guest") + // Expect(err).ShouldNot(HaveOccurred()) + // }) + + // for _, tc := range tests { + // It(fmt.Sprintf("should handle MQTT version %s", tc.connectionVersion), func() { + // rootCmd := RootCmd() + + // args := []string{"mqtt", + // "-z", "7s", + // "-r", "1"} + // if tc.versionFlag != "" { + // args = append(args, "--mqtt-publisher-version", tc.versionFlag) + // args = append(args, "--mqtt-consumer-version", tc.versionFlag) + // } + // rootCmd.SetArgs(args) + // fmt.Println("Running test: omq", strings.Join(args, " ")) + + // var wg sync.WaitGroup + // wg.Add(1) + // ctx, cancel := context.WithCancel(context.Background()) + // defer cancel() + + // go func() { + // defer wg.Done() + // session, err := gexec.Start(rootCmd, GinkgoWriter, GinkgoWriter) + // Expect(err).ShouldNot(HaveOccurred()) + // Eventually(session).Should(gexec.Exit(0)) + // }() + + // Eventually(func() bool { + // conns, err := rmqc.ListConnections() + // return err == nil && + // len(conns) >= 2 && + // slices.ContainsFunc(conns, func(conn rabbithole.ConnectionInfo) bool { + // return conn.Protocol == tc.connectionVersion + // }) + // }, 7*time.Second, 500*time.Millisecond).Should(BeTrue()) + + // cancel() + // wg.Wait() + // time.Sleep(3 * time.Second) + // }) + // } + // }) + + // Describe("end-to-end latency uses nanoseconds only if we have both publishers and consumers", func() { + // It("should use nanoseconds when no -x / -y flags are used", func() { + // args := []string{"amqp", "-C", "0", "-D", "0"} + // rootCmd := cmd.RootCmd() + // rootCmd.SetArgs(args) + // _ = rootCmd.Execute() + // Expect(cfg.UseMillis).To(BeFalse()) + // }) + + // It("should use millseconds when there are no publishers", func() { + // args := []string{"amqp", "-x", "0", "-D", "0"} + // rootCmd := RootCmd() + // rootCmd.SetArgs(args) + // _ = rootCmd.Execute() + // Expect(cfg.UseMillis).To(BeTrue()) + // }) + + // It("should use millseconds when there are no consumers", func() { + // args := []string{"amqp", "-y", "0", "-C", "0"} + // rootCmd := RootCmd() + // rootCmd.SetArgs(args) + // _ = rootCmd.Execute() + // Expect(cfg.UseMillis).To(BeTrue()) + // }) + // }) +}) diff --git a/omq_suite_test.go b/omq_suite_test.go new file mode 100644 index 0000000..b33640b --- /dev/null +++ b/omq_suite_test.go @@ -0,0 +1,22 @@ +package main_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "github.com/onsi/gomega/gexec" +) + +func TestOmq(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "OMQ Suite") +} + +var omq string +var _ = BeforeSuite(func() { + var err error + omq, err = gexec.Build("github.com/rabbitmq/omq") + Expect(err).NotTo(HaveOccurred()) + DeferCleanup(gexec.CleanupBuildArtifacts) +}) diff --git a/pkg/config/config.go b/pkg/config/config.go index e689532..4f1688d 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -97,6 +97,7 @@ type Config struct { MqttConsumer MqttOptions MetricTags map[string]string LogOutOfOrder bool + PrintAllMetrics bool ConsumerStartupDelay time.Duration } diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index ac90e96..560fa20 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "io" "maps" "net/http" "runtime" @@ -148,7 +149,7 @@ func (m *MetricsServer) PrintMessageRates(ctx context.Context) { }() } -func (m *MetricsServer) PrintFinalMetrics() { +func (m *MetricsServer) PrintSummary() { // this might ve called before the metrics were registered // eg. by `omq --help` if MessagesPublished == nil { @@ -159,9 +160,30 @@ func (m *MetricsServer) PrintFinalMetrics() { "messages", MessagesPublished.Get(), "rate", fmt.Sprintf("%.2f/s", float64(MessagesPublished.Get())/time.Since(m.started).Seconds())) log.Print("TOTAL CONSUMED", - "consumed", MessagesConsumedNormalPriority.Get()+MessagesConsumedHighPriority.Get(), + "messages", MessagesConsumedNormalPriority.Get()+MessagesConsumedHighPriority.Get(), "rate", fmt.Sprintf("%.2f/s", float64(MessagesConsumedNormalPriority.Get()+MessagesConsumedHighPriority.Get())/time.Since(m.started).Seconds())) +} +func (m MetricsServer) PrintAll() { + endpoint := fmt.Sprintf("http://%s/metrics", m.httpServer.Addr) + resp, err := http.Get(endpoint) + if err != nil { + return + } + defer func() { _ = resp.Body.Close() }() + + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Error("Error reading metrics", "error", err) + return + } + + metrics := strings.Split(string(body), "\n") + for _, metric := range metrics { + if strings.HasPrefix(metric, "omq_") { + fmt.Println(metric) + } + } } func get_metrics_ip() string { diff --git a/pkg/mgmt/mgmt_suite_test.go b/pkg/mgmt/mgmt_suite_test.go new file mode 100644 index 0000000..65d7a08 --- /dev/null +++ b/pkg/mgmt/mgmt_suite_test.go @@ -0,0 +1,13 @@ +package mgmt_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestMgmt(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "MGMT Suite") +} diff --git a/pkg/mgmt/mgmt_test.go b/pkg/mgmt/mgmt_test.go index c81d07f..b94ef7a 100644 --- a/pkg/mgmt/mgmt_test.go +++ b/pkg/mgmt/mgmt_test.go @@ -1,43 +1,52 @@ -package mgmt +package mgmt_test import ( "context" - "testing" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "github.com/rabbitmq/omq/pkg/config" "github.com/rabbitmq/omq/pkg/log" - "github.com/stretchr/testify/assert" + "github.com/rabbitmq/omq/pkg/mgmt" ) -func TestDeclareAndBindPredeclared(t *testing.T) { - cfg := config.Config{Queues: config.Predeclared, PublishTo: "foobar"} - q := DeclareAndBind(cfg, "test", 0) - assert.Nil(t, q) -} +var _ = Describe("DeclareAndBind", func() { + It("should not declare anything when using predeclared queues", func() { + cfg := config.Config{Queues: config.Predeclared, PublishTo: "foobar"} + q := mgmt.DeclareAndBind(cfg, "test", 0) + Expect(q).To(BeNil()) + }) -func TestDeclareAndBindNoId(t *testing.T) { - log.Setup() - cfg := config.Config{Queues: config.Classic, PublishTo: "/queues/foobar"} - q := DeclareAndBind(cfg, "foobar", 0) - assert.Equal(t, "foobar", q.GetName()) - err := Get().Queue("foobar").Delete(context.Background()) - assert.Nil(t, err) -} + It("should declare and bind a classic queue", func() { + log.Setup() + cfg := config.Config{Queues: config.Classic, PublishTo: "/queues/mgmt-classic"} + q := mgmt.DeclareAndBind(cfg, "mgmt-classic", 0) + Expect(q.GetName()).To(Equal("mgmt-classic")) + Expect(string(q.Type())).To(Equal("classic")) + err := mgmt.Get().Queue("mgmt-classic").Delete(context.Background()) + Expect(err).To(BeNil()) + // TOOD assert the binding + }) -func TestDeclareAndBindWithId(t *testing.T) { - log.Setup() - cfg := config.Config{Queues: config.Classic, PublishTo: "/queues/foobar"} - q := DeclareAndBind(cfg, "foobar-123", 123) - assert.Equal(t, "foobar-123", q.GetName()) - err := Get().Queue("foobar-123").Delete(context.Background()) - assert.Nil(t, err) -} + It("should declare and bind a quorum queue", func() { + log.Setup() + cfg := config.Config{Queues: config.Quorum, PublishTo: "/queues/mgmt-quorum"} + q := mgmt.DeclareAndBind(cfg, "mgmt-quorum", 0) + Expect(q.GetName()).To(Equal("mgmt-quorum")) + Expect(string(q.Type())).To(Equal("quorum")) + err := mgmt.Get().Queue("mgmt-quorum").Delete(context.Background()) + Expect(err).To(BeNil()) + // TOOD assert the binding + }) -func TestDeclareAndBindStompAmqp(t *testing.T) { - log.Setup() - cfg := config.Config{Queues: config.Classic, PublishTo: "/topic/stompstomp", ConsumeFrom: "/topic/stompamqp"} - q := DeclareAndBind(cfg, "foobar-123", 123) - assert.Equal(t, "foobar-123", q.GetName()) - err := Get().Queue("foobar-123").Delete(context.Background()) - assert.Nil(t, err) -} + It("should declare and bind a stream queue", func() { + log.Setup() + cfg := config.Config{Queues: config.Stream, PublishTo: "/queues/mgmt-stream"} + q := mgmt.DeclareAndBind(cfg, "mgmt-stream", 0) + Expect(q.GetName()).To(Equal("mgmt-stream")) + Expect(string(q.Type())).To(Equal("stream")) + err := mgmt.Get().Queue("mgmt-stream").Delete(context.Background()) + Expect(err).To(BeNil()) + // TOOD assert the binding + }) +}) diff --git a/pkg/utils/utils_suite_test.go b/pkg/utils/utils_suite_test.go new file mode 100644 index 0000000..8ae2eeb --- /dev/null +++ b/pkg/utils/utils_suite_test.go @@ -0,0 +1,13 @@ +package utils_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestUtils(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "UTILS Suite") +} diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go index 05196cd..22e9d2c 100644 --- a/pkg/utils/utils_test.go +++ b/pkg/utils/utils_test.go @@ -2,59 +2,77 @@ package utils_test import ( "strconv" - "testing" + "time" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" "github.com/rabbitmq/omq/pkg/utils" - "github.com/stretchr/testify/assert" ) -func TestURIParsing(t *testing.T) { - type test struct { - rawURI string - defaultScheme string - defaultPort string - broker string - username string - password string - } - - tests := []test{ - {rawURI: "mqtt://user:pass@name.com", defaultScheme: "mqtt", defaultPort: "1234", broker: "name.com:1234", username: "user", password: "pass"}, - {rawURI: "mqtt://name.com", defaultScheme: "mqtt", defaultPort: "1234", broker: "name.com:1234", username: "guest", password: "guest"}, - {rawURI: "mqtts://local:4321", defaultScheme: "mqtt", defaultPort: "1234", broker: "local:4321", username: "guest", password: "guest"}, - {rawURI: "local:4321", defaultScheme: "mqtt", defaultPort: "1234", broker: "local:4321", username: "guest", password: "guest"}, - } - - for _, tc := range tests { - t.Run(tc.rawURI+"-"+tc.defaultPort, func(t *testing.T) { - parsed := utils.ParseURI(tc.rawURI, tc.defaultScheme, tc.defaultPort) - assert.Equal(t, tc.broker, parsed.Broker) - assert.Equal(t, tc.username, parsed.Username) - assert.Equal(t, tc.password, parsed.Password) - }) - } -} - -func TestWrappedSequence(t *testing.T) { - type test struct { - length int - start int - expectedSequence []int - } - - tests := []test{ - {length: 5, start: 0, expectedSequence: []int{0, 1, 2, 3, 4}}, - {length: 5, start: 1, expectedSequence: []int{1, 2, 3, 4, 0}}, - {length: 3, start: 1, expectedSequence: []int{1, 2, 0}}, - {length: 3, start: 4, expectedSequence: []int{1, 2, 0}}, - {length: 3, start: 5, expectedSequence: []int{2, 0, 1}}, - {length: 1, start: 2, expectedSequence: []int{0}}, - } - - for n, tc := range tests { - t.Run(strconv.Itoa(n), func(t *testing.T) { - assert.Equal(t, tc.expectedSequence, utils.WrappedSequence(tc.length, tc.start)) - }) - } - -} +var _ = Context("Utils", func() { + DescribeTable("Latency calculation", + func(units string, useMillis bool) { + testMsg := utils.MessageBody(100) + utils.UpdatePayload(useMillis, &testMsg) + time.Sleep(10 * time.Millisecond) + _, latency := utils.CalculateEndToEndLatency(&testMsg) + // not very precise but we just care about the order of magnitude + Expect(latency.Milliseconds()).Should(BeNumerically(">", 9)) + Expect(latency.Milliseconds()).Should(BeNumerically("<", 50)) + }, + Entry("When using nanoseconds", "nanoseconds", false), + Entry("When using milliseconds", "milliseconds", true), + ) + + Describe("URI Parsing", func() { + type test struct { + rawURI string + defaultScheme string + defaultPort string + broker string + username string + password string + } + + tests := []test{ + {rawURI: "mqtt://user:pass@name.com", defaultScheme: "mqtt", defaultPort: "1234", broker: "name.com:1234", username: "user", password: "pass"}, + {rawURI: "mqtt://name.com", defaultScheme: "mqtt", defaultPort: "1234", broker: "name.com:1234", username: "guest", password: "guest"}, + {rawURI: "mqtts://local:4321", defaultScheme: "mqtt", defaultPort: "1234", broker: "local:4321", username: "guest", password: "guest"}, + {rawURI: "local:4321", defaultScheme: "mqtt", defaultPort: "1234", broker: "local:4321", username: "guest", password: "guest"}, + } + + for _, tc := range tests { + tc := tc + It("should parse URI "+tc.rawURI+"-"+tc.defaultPort, func() { + parsed := utils.ParseURI(tc.rawURI, tc.defaultScheme, tc.defaultPort) + Expect(parsed.Broker).To(Equal(tc.broker)) + Expect(parsed.Username).To(Equal(tc.username)) + Expect(parsed.Password).To(Equal(tc.password)) + }) + } + }) + + Describe("Wrapped Sequence", func() { + type test struct { + length int + start int + expectedSequence []int + } + + tests := []test{ + {length: 5, start: 0, expectedSequence: []int{0, 1, 2, 3, 4}}, + {length: 5, start: 1, expectedSequence: []int{1, 2, 3, 4, 0}}, + {length: 3, start: 1, expectedSequence: []int{1, 2, 0}}, + {length: 3, start: 4, expectedSequence: []int{1, 2, 0}}, + {length: 3, start: 5, expectedSequence: []int{2, 0, 1}}, + {length: 1, start: 2, expectedSequence: []int{0}}, + } + + for n, tc := range tests { + tc := tc + It("should generate wrapped sequence "+strconv.Itoa(n), func() { + Expect(utils.WrappedSequence(tc.length, tc.start)).To(Equal(tc.expectedSequence)) + }) + } + }) +})