From 3856cbee61d4ef121656a6ed61807497cd145a2e Mon Sep 17 00:00:00 2001 From: Michal Kuratczyk Date: Mon, 18 Nov 2024 08:31:40 +0100 Subject: [PATCH] Switch tests to ginkgo It's easier to test CLIs with ginkgo and it makes it easy to assert on the output, rather than metrics. Validating metrics is tricky, because tests are run concurrently --- .github/workflows/ci.yaml | 6 +- cmd/cmd_suite_test.go | 13 + cmd/cmd_test.go | 472 ++-------------------------------- cmd/root.go | 16 +- go.mod | 11 +- go.sum | 208 +-------------- main.go | 3 - main_test.go | 345 +++++++++++++++++++++++++ omq_suite_test.go | 22 ++ pkg/config/config.go | 1 + pkg/metrics/metrics.go | 26 +- pkg/mgmt/mgmt_suite_test.go | 13 + pkg/mgmt/mgmt_test.go | 73 +++--- pkg/utils/utils_suite_test.go | 13 + pkg/utils/utils_test.go | 122 +++++---- 15 files changed, 590 insertions(+), 754 deletions(-) create mode 100644 cmd/cmd_suite_test.go create mode 100644 main_test.go create mode 100644 omq_suite_test.go create mode 100644 pkg/mgmt/mgmt_suite_test.go create mode 100644 pkg/utils/utils_suite_test.go 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)) + }) + } + }) +})