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) } }