From 9a435cdc3e90f6014bc6f670989d715b62c6d7b6 Mon Sep 17 00:00:00 2001 From: hermes Date: Wed, 30 Sep 2026 21:59:32 +0200 Subject: [PATCH] add internal/infra/mqtt_test.go --- internal/infra/mqtt_test.go | 76 +++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 internal/infra/mqtt_test.go diff --git a/internal/infra/mqtt_test.go b/internal/infra/mqtt_test.go new file mode 100644 index 0000000..5c01c9d --- /dev/null +++ b/internal/infra/mqtt_test.go @@ -0,0 +1,76 @@ +package infra + +import ( + "bytes" + "testing" +) + +func TestMQTTConnectPacket(t *testing.T) { + p := mqttConnect("radio-go", "user1", "pass1") + // fixed header: 0x10, remaining length + if p[0] != 0x10 { + t.Fatalf("packet type = 0x%02x, want 0x10", p[0]) + } + rl := int(p[1]) + if rl != len(p)-2 { + t.Fatalf("remaining length %d != %d", rl, len(p)-2) + } + body := string(p[2:]) + for _, want := range []string{"MQTT", "user1", "pass1", "radio-go"} { + if !bytes.Contains(p, []byte(want)) { + t.Fatalf("connect packet missing %q: %q", want, body) + } + } + // protocol level 4 (3.1.1): fixed hdr 2B + name len 2B + "MQTT" 4B => index 8 + if p[8] != 4 { + t.Fatalf("protocol level = %d, want 4", p[8]) + } + // connect flags: clean-session(0x02) | username(0x80) | password(0x40) + if p[9] != 0xC2 { + t.Fatalf("connect flags = 0x%02x, want 0xC2", p[9]) + } +} + +func TestMQTTConnectNoAuth(t *testing.T) { + p := mqttConnect("c1", "", "") + if p[9]&0xC0 != 0 { + t.Fatalf("flags should have no user/pass bits: 0x%02x", p[9]) + } +} + +func TestMQTTPublishPacket(t *testing.T) { + topic := "airstudio/nowplaying" + payload := `{"title":"Neon Rain"}` + p := mqttPublish(topic, []byte(payload)) + // QoS 0 PUBLISH => 0x30 + if p[0] != 0x30 { + t.Fatalf("packet type = 0x%02x, want 0x30", p[0]) + } + rl := int(p[1]) + if rl != len(p)-2 { + t.Fatalf("remaining length %d != %d", rl, len(p)-2) + } + // topic is first: 2-byte length + bytes + tl := int(p[2])<<8 | int(p[3]) + if got := string(p[4 : 4+tl]); got != topic { + t.Fatalf("topic = %q, want %q", got, topic) + } + if got := string(p[4+tl:]); got != payload { + t.Fatalf("payload = %q, want %q", got, payload) + } +} + +func TestMQTTRemainingLengthMultiByte(t *testing.T) { + big := bytes.Repeat([]byte("x"), 200) + p := mqttPublish("t", big) + // 200 + topic overhead > 127 => two-byte remaining length + if p[1]&0x80 == 0 { + t.Fatalf("expected continuation bit set for >127 remaining, got 0x%02x", p[1]) + } + // MQTT remaining length is base-128 (not bit-shifted): low7 + next*128 + rl := int(p[1]&0x7F) + int(p[2])*128 + want := 2 + len("t") + len(big) // topic len + topic + payload (QoS0, no packet id) + if rl != want { + t.Fatalf("remaining length %d, want %d", rl, want) + } +}