@@ -794,6 +794,73 @@ TEST(encode_publish_qos0_no_flags_in_header)
794794 ASSERT_EQ (0 , (int )MQTT_PACKET_FLAGS_GET (tx_buf [0 ]));
795795}
796796
797+ /* [MQTT-3.3.1-2] DUP MUST be 0 for all QoS 0 messages. The encoder must
798+ * refuse the forbidden combination at the API boundary so the library can't
799+ * produce a wire packet that the decoder (and any spec-compliant receiver)
800+ * would reject as malformed. */
801+ TEST (encode_publish_qos0_with_dup_rejected )
802+ {
803+ byte tx_buf [64 ];
804+ byte payload [] = { 'x' };
805+ MqttPublish pub ;
806+ int rc ;
807+
808+ XMEMSET (& pub , 0 , sizeof (pub ));
809+ pub .topic_name = "a" ;
810+ pub .qos = MQTT_QOS_0 ;
811+ pub .duplicate = 1 ;
812+ pub .buffer = payload ;
813+ pub .total_len = sizeof (payload );
814+
815+ rc = MqttEncode_Publish (tx_buf , (int )sizeof (tx_buf ), & pub , 0 );
816+ ASSERT_EQ (MQTT_CODE_ERROR_BAD_ARG , rc );
817+ }
818+
819+ /* QoS 1 with DUP=1 is a legitimate retransmission shape per [MQTT-4.3.2].
820+ * Pin that the new check is QoS-0-specific and doesn't break retransmits. */
821+ TEST (encode_publish_qos1_with_dup_accepted )
822+ {
823+ byte tx_buf [64 ];
824+ byte payload [] = { 'x' };
825+ MqttPublish pub ;
826+ int rc ;
827+
828+ XMEMSET (& pub , 0 , sizeof (pub ));
829+ pub .topic_name = "a" ;
830+ pub .qos = MQTT_QOS_1 ;
831+ pub .packet_id = 42 ;
832+ pub .duplicate = 1 ;
833+ pub .buffer = payload ;
834+ pub .total_len = sizeof (payload );
835+
836+ rc = MqttEncode_Publish (tx_buf , (int )sizeof (tx_buf ), & pub , 0 );
837+ ASSERT_TRUE (rc > 0 );
838+ /* Fixed-header low nibble: DUP|QoS1 = 0x8 | 0x2 = 0xA. */
839+ ASSERT_EQ (0xA , (int )MQTT_PACKET_FLAGS_GET (tx_buf [0 ]));
840+ }
841+
842+ /* QoS 2 with DUP=1 is also a legitimate retransmission shape per [MQTT-4.3.3]. */
843+ TEST (encode_publish_qos2_with_dup_accepted )
844+ {
845+ byte tx_buf [64 ];
846+ byte payload [] = { 'x' };
847+ MqttPublish pub ;
848+ int rc ;
849+
850+ XMEMSET (& pub , 0 , sizeof (pub ));
851+ pub .topic_name = "a" ;
852+ pub .qos = MQTT_QOS_2 ;
853+ pub .packet_id = 42 ;
854+ pub .duplicate = 1 ;
855+ pub .buffer = payload ;
856+ pub .total_len = sizeof (payload );
857+
858+ rc = MqttEncode_Publish (tx_buf , (int )sizeof (tx_buf ), & pub , 0 );
859+ ASSERT_TRUE (rc > 0 );
860+ /* DUP|QoS2 = 0x8 | 0x4 = 0xC. */
861+ ASSERT_EQ (0xC , (int )MQTT_PACKET_FLAGS_GET (tx_buf [0 ]));
862+ }
863+
797864/* f-2360: topic_name with strlen > 65535 must not produce a "successful"
798865 * encode. MqttEncode_String returns -1 for oversize strings; the encoder
799866 * must surface that as a negative return rather than adding -1 to the
@@ -2783,6 +2850,9 @@ void run_mqtt_packet_tests(void)
27832850 RUN_TEST (encode_publish_qos1_retain_flags_in_header );
27842851 RUN_TEST (encode_publish_qos2_duplicate_flags_in_header );
27852852 RUN_TEST (encode_publish_qos0_no_flags_in_header );
2853+ RUN_TEST (encode_publish_qos0_with_dup_rejected );
2854+ RUN_TEST (encode_publish_qos1_with_dup_accepted );
2855+ RUN_TEST (encode_publish_qos2_with_dup_accepted );
27862856 RUN_TEST (encode_publish_topic_oversized_rejected );
27872857
27882858 /* MqttDecode_Publish */
0 commit comments