mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
49488738df
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
Signed-off-by: dusan <borovcanindusan1@gmail.com>
265 lines
6.1 KiB
Go
265 lines
6.1 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package fluxmq
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestStreamFilter(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
topic string
|
|
want string
|
|
}{
|
|
{
|
|
name: "all messages with prefix",
|
|
prefix: "m",
|
|
topic: "m/#",
|
|
want: "#",
|
|
},
|
|
{
|
|
name: "all messages without explicit prefix",
|
|
prefix: "writers",
|
|
topic: "#",
|
|
want: "#",
|
|
},
|
|
{
|
|
name: "specific topic filter",
|
|
prefix: "writers",
|
|
topic: "writers/domain/c/channel/+",
|
|
want: "domain/c/channel/+",
|
|
},
|
|
{
|
|
name: "topic without prefix",
|
|
prefix: "alarms",
|
|
topic: "domain/c/channel/#",
|
|
want: "domain/c/channel/#",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := streamFilter(tc.prefix, tc.topic)
|
|
if got != tc.want {
|
|
t.Fatalf("stream filter mismatch: got %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestQueueFilter(t *testing.T) {
|
|
got := queueFilter("writers", "writers/#")
|
|
want := "$queue/writers/#"
|
|
if got != want {
|
|
t.Fatalf("queue filter mismatch: got %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestTopicFilter(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
topic string
|
|
want string
|
|
}{
|
|
{
|
|
name: "all messages with prefix",
|
|
prefix: "m",
|
|
topic: "m/#",
|
|
want: "m/#",
|
|
},
|
|
{
|
|
name: "wildcard topic",
|
|
prefix: "writers",
|
|
topic: "#",
|
|
want: "writers/#",
|
|
},
|
|
{
|
|
name: "specific topic",
|
|
prefix: "m",
|
|
topic: "m/domain/c/channel/subtopic",
|
|
want: "m/domain/c/channel/subtopic",
|
|
},
|
|
{
|
|
name: "single-level wildcard",
|
|
prefix: "m",
|
|
topic: "m/domain/c/+/subtopic",
|
|
want: "m/domain/c/+/subtopic",
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
got := topicFilter(tc.prefix, tc.topic)
|
|
if got != tc.want {
|
|
t.Fatalf("topic filter mismatch: got %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseMQTTTopic(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
prefix string
|
|
topic string
|
|
domain string
|
|
channel string
|
|
subtopic string
|
|
shouldErr bool
|
|
}{
|
|
{
|
|
name: "default prefix with subtopic path",
|
|
prefix: "m",
|
|
topic: "m/domain/c/channel/sub/topic",
|
|
domain: "domain",
|
|
channel: "channel",
|
|
subtopic: "sub/topic",
|
|
},
|
|
{
|
|
name: "alternate prefix without subtopic",
|
|
prefix: "writers",
|
|
topic: "writers/domain/c/channel",
|
|
domain: "domain",
|
|
channel: "channel",
|
|
subtopic: "",
|
|
},
|
|
{
|
|
name: "leading slash is ignored",
|
|
prefix: "alarms",
|
|
topic: "/alarms/domain/c/channel/critical/high",
|
|
domain: "domain",
|
|
channel: "channel",
|
|
subtopic: "critical/high",
|
|
},
|
|
{
|
|
name: "mismatched prefix",
|
|
prefix: "writers",
|
|
topic: "m/domain/c/channel",
|
|
shouldErr: true,
|
|
},
|
|
{
|
|
name: "invalid shape",
|
|
prefix: "m",
|
|
topic: "m/domain/channel",
|
|
shouldErr: true,
|
|
},
|
|
{
|
|
name: "empty subtopic segment",
|
|
prefix: "m",
|
|
topic: "m/domain/c/channel/sub//topic",
|
|
shouldErr: true,
|
|
},
|
|
{
|
|
name: "dot topic is invalid",
|
|
prefix: "m",
|
|
topic: "m.domain.c.channel.sub.topic",
|
|
shouldErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
domain, channel, subtopic, err := parseMQTTTopic(tc.prefix, tc.topic)
|
|
if tc.shouldErr {
|
|
if err == nil {
|
|
t.Fatal("expected parse error, got nil")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected parse error: %v", err)
|
|
}
|
|
if domain != tc.domain || channel != tc.channel || subtopic != tc.subtopic {
|
|
t.Fatalf("parsed topic mismatch: got domain=%q channel=%q subtopic=%q", domain, channel, subtopic)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseMQTTTopicFromStreamRoutingKey(t *testing.T) {
|
|
// Stream queue routing keys have the format "$queue/<prefix>/<domain>/c/<channel>[/<subtopic>]".
|
|
// After stripping "$queue/", the remainder is a valid MQTT-style topic for parseMQTTTopic.
|
|
cases := []struct {
|
|
name string
|
|
routingKey string
|
|
prefix string
|
|
domain string
|
|
channel string
|
|
subtopic string
|
|
}{
|
|
{
|
|
name: "writers queue with subtopic",
|
|
routingKey: "$queue/writers/domain/c/channel/temp",
|
|
prefix: "writers",
|
|
domain: "domain",
|
|
channel: "channel",
|
|
subtopic: "temp",
|
|
},
|
|
{
|
|
name: "main queue without subtopic",
|
|
routingKey: "$queue/m/domain/c/channel",
|
|
prefix: "m",
|
|
domain: "domain",
|
|
channel: "channel",
|
|
subtopic: "",
|
|
},
|
|
{
|
|
name: "alarms queue with nested subtopic",
|
|
routingKey: "$queue/alarms/dom/c/ch/critical/high",
|
|
prefix: "alarms",
|
|
domain: "dom",
|
|
channel: "ch",
|
|
subtopic: "critical/high",
|
|
},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
mqttTopic := strings.TrimPrefix(tc.routingKey, "$queue/")
|
|
domain, channel, subtopic, err := parseMQTTTopic(tc.prefix, mqttTopic)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if domain != tc.domain || channel != tc.channel || subtopic != tc.subtopic {
|
|
t.Fatalf("got domain=%q channel=%q subtopic=%q", domain, channel, subtopic)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStringHeader(t *testing.T) {
|
|
headers := map[string]any{
|
|
"external_id": "pub-1",
|
|
"number": 42,
|
|
"bytes": []byte("bin"),
|
|
}
|
|
if got := stringHeader(headers, "external_id"); got != "pub-1" {
|
|
t.Fatalf("expected pub-1, got %q", got)
|
|
}
|
|
if got := stringHeader(headers, "bytes"); got != "bin" {
|
|
t.Fatalf("expected bin, got %q", got)
|
|
}
|
|
if got := stringHeader(headers, "number"); got != "" {
|
|
t.Fatalf("expected empty for non-string, got %q", got)
|
|
}
|
|
if got := stringHeader(headers, "missing"); got != "" {
|
|
t.Fatalf("expected empty for missing key, got %q", got)
|
|
}
|
|
if got := stringHeader(nil, "any"); got != "" {
|
|
t.Fatalf("expected empty for nil headers, got %q", got)
|
|
}
|
|
}
|
|
|
|
func TestFormatConsumerName(t *testing.T) {
|
|
got := formatConsumerName("m/domain/c/channel/#", "re/service 1")
|
|
want := "m_domain_c_channel__-re_service_1"
|
|
if got != want {
|
|
t.Fatalf("consumer name mismatch: got %q, want %q", got, want)
|
|
}
|
|
}
|