@@ -601,6 +601,8 @@ func (e *mockError) Error() string {
601601
602602func TestBuildHostOptionsWithAnnounceAddrs (t * testing.T ) {
603603 logger := & DefaultLogger {}
604+ // noop cancel function for testing - buildHostOptions requires a cancel function
605+ // but we don't need actual cancellation logic in this unit test
604606 cancel := func () {}
605607
606608 tests := []struct {
@@ -725,133 +727,114 @@ func TestClientPeerCacheTTLCustom(t *testing.T) {
725727 require .NoError (t , err )
726728}
727729
728- // FuzzMessageUnmarshal performs fuzz testing on the P2P message unmarshaling logic
729- // to ensure it handles arbitrary JSON data without panicking. This tests the
730- // robustness of message parsing from potentially malicious or corrupted peers.
731- func FuzzMessageUnmarshal (f * testing.F ) {
732- // Seed corpus with various P2P message formats
733-
734- // 1. Valid message
735- f .Add ([]byte (`{"name":"alice","data":"aGVsbG8="}` )) // "hello" in base64
730+ // messageStruct represents the P2P message structure for testing
731+ type messageStruct struct {
732+ Name string `json:"name"`
733+ Data []byte `json:"data"`
734+ }
736735
737- // 2. Valid message with empty data
736+ // addMessageUnmarshalSeeds adds a comprehensive seed corpus for P2P message fuzz testing
737+ func addMessageUnmarshalSeeds (f * testing.F ) {
738+ // Valid messages
739+ f .Add ([]byte (`{"name":"alice","data":"aGVsbG8="}` ))
738740 f .Add ([]byte (`{"name":"bob","data":""}` ))
739-
740- // 3. Valid message with null data
741741 f .Add ([]byte (`{"name":"charlie","data":null}` ))
742742
743- // 4. Missing name field
743+ // Missing fields
744744 f .Add ([]byte (`{"data":"aGVsbG8="}` ))
745-
746- // 5. Missing data field
747745 f .Add ([]byte (`{"name":"alice"}` ))
748-
749- // 6. Empty JSON object
750746 f .Add ([]byte (`{}` ))
751747
752- // 7. Invalid JSON
748+ // Invalid JSON
753749 f .Add ([]byte (`{invalid json` ))
754750 f .Add ([]byte (`}` ))
755751 f .Add ([]byte (`{"name":"alice","data":` ))
756752
757- // 8. Wrong data types
753+ // Wrong data types
758754 f .Add ([]byte (`{"name":123,"data":"test"}` ))
759755 f .Add ([]byte (`{"name":"alice","data":123}` ))
760756 f .Add ([]byte (`{"name":"alice","data":{"nested":"object"}}` ))
761757 f .Add ([]byte (`{"name":"alice","data":["array","of","items"]}` ))
762758
763- // 9. Not an object
759+ // Not an object
764760 f .Add ([]byte (`[]` ))
765761 f .Add ([]byte (`"string"` ))
766762 f .Add ([]byte (`123` ))
767763 f .Add ([]byte (`null` ))
768764 f .Add ([]byte (`true` ))
769765
770- // 10. Extra fields
766+ // Extra fields
771767 f .Add ([]byte (`{"name":"alice","data":"test","extra":"field","another":123}` ))
772768
773- // 11. Very long strings
769+ // Very long strings
774770 longName := `{"name":"` + string (make ([]byte , 1000 )) + `","data":"test"}`
775771 f .Add ([]byte (longName ))
776772
777- // 12. Unicode and special characters (using escape sequences to avoid gosmopolitan warning)
778- f .Add ([]byte (`{"name":"peer\u4f60\u597d\u4e16\u754c","data":"test"}` )) // Chinese characters
773+ // Unicode and special characters
774+ f .Add ([]byte (`{"name":"peer\u4f60\u597d\u4e16\u754c","data":"test"}` ))
779775 f .Add ([]byte (`{"name":"alice\u0000null","data":"test"}` ))
780776 f .Add ([]byte (`{"name":"alice","data":"data\u0000null"}` ))
781777
782- // 13. Escape sequences
778+ // Escape sequences
783779 f .Add ([]byte (`{"name":"alice\"bob","data":"test"}` ))
784780 f .Add ([]byte (`{"name":"alice\\bob","data":"test"}` ))
785781
786- // 14. Empty string
782+ // Edge cases
787783 f .Add ([]byte (`` ))
788-
789- // 15. Nested JSON in data field (as string)
790784 f .Add ([]byte (`{"name":"alice","data":"{\"nested\":\"json\"}"}` ))
791-
792- // 16. Binary data encoded as base64 (proper use case)
793785 f .Add ([]byte (`{"name":"alice","data":"SGVsbG8gV29ybGQhIFRoaXMgaXMgYSB0ZXN0IG1lc3NhZ2Uu"}` ))
786+ }
794787
795- f .Fuzz (func (t * testing.T , jsonData []byte ) {
796- // Simulate the message unmarshaling logic from client.go:616-623
797- var m struct {
798- Name string `json:"name"`
799- Data []byte `json:"data"`
800- }
788+ // validateMessageRoundTrip verifies that message data is preserved through marshal/unmarshal cycle
789+ func validateMessageRoundTrip (t * testing.T , m , m2 messageStruct ) {
790+ if m .Name != m2 .Name {
791+ t .Errorf ("Name changed after round-trip: %q -> %q" , m .Name , m2 .Name )
792+ }
801793
802- // The function should never panic, regardless of input
803- err := json .Unmarshal (jsonData , & m )
804- // Verify behavior is consistent
805- if err != nil {
806- // Error is expected for invalid JSON - the client code logs this error and continues
807- // We just verified no panic occurred, which is the main goal
808- return
794+ if len (m .Data ) != len (m2 .Data ) {
795+ t .Errorf ("Data length changed after round-trip: %d -> %d" , len (m .Data ), len (m2 .Data ))
796+ return
797+ }
798+
799+ for i := range m .Data {
800+ if m .Data [i ] != m2 .Data [i ] {
801+ t .Errorf ("Data byte at index %d changed after round-trip: %d -> %d" , i , m .Data [i ], m2 .Data [i ])
802+ break
809803 }
804+ }
805+ }
806+
807+ // FuzzMessageUnmarshal performs fuzz testing on the P2P message unmarshaling logic
808+ // to ensure it handles arbitrary JSON data without panicking. This tests the
809+ // robustness of message parsing from potentially malicious or corrupted peers.
810+ func FuzzMessageUnmarshal (f * testing.F ) {
811+ addMessageUnmarshalSeeds (f )
810812
811- // Name should be a string (could be empty)
812- _ = m . Name
813+ f . Fuzz ( func ( t * testing. T , jsonData [] byte ) {
814+ var m messageStruct
813815
814- // Data should be a byte slice (could be nil or empty)
815- if m .Data != nil {
816- // Data exists, verify it's a valid byte slice
817- _ = len (m .Data )
816+ err := json .Unmarshal (jsonData , & m )
817+ if err != nil {
818+ return // Invalid JSON is expected and handled gracefully
818819 }
819820
820- // Test that we can re-marshal the data
821- remarshaled , marshalErr := json . Marshal ( m )
822- if marshalErr != nil {
823- t . Errorf ( "Failed to re-marshal successfully unmarshaled message: %v" , marshalErr )
821+ remarshaled , err := json . Marshal ( m )
822+ if err != nil {
823+ t . Errorf ( "Failed to re-marshal successfully unmarshaled message: %v" , err )
824+ return
824825 }
825826
826- // Re-marshaled data should be valid JSON
827827 if len (remarshaled ) == 0 {
828828 t .Error ("Re-marshaled data is empty" )
829+ return
829830 }
830831
831- // Test that we can unmarshal the re-marshaled data
832- var m2 struct {
833- Name string `json:"name"`
834- Data []byte `json:"data"`
835- }
836- if unmarshalErr := json .Unmarshal (remarshaled , & m2 ); unmarshalErr != nil {
837- t .Errorf ("Failed to unmarshal re-marshaled data: %v" , unmarshalErr )
838- }
839-
840- // Round-trip should preserve the data
841- if m .Name != m2 .Name {
842- t .Errorf ("Name changed after round-trip: %q -> %q" , m .Name , m2 .Name )
832+ var m2 messageStruct
833+ if err := json .Unmarshal (remarshaled , & m2 ); err != nil {
834+ t .Errorf ("Failed to unmarshal re-marshaled data: %v" , err )
835+ return
843836 }
844837
845- // For data, compare lengths and content
846- if len (m .Data ) != len (m2 .Data ) {
847- t .Errorf ("Data length changed after round-trip: %d -> %d" , len (m .Data ), len (m2 .Data ))
848- } else {
849- for i := range m .Data {
850- if m .Data [i ] != m2 .Data [i ] {
851- t .Errorf ("Data byte at index %d changed after round-trip: %d -> %d" , i , m .Data [i ], m2 .Data [i ])
852- break
853- }
854- }
855- }
838+ validateMessageRoundTrip (t , m , m2 )
856839 })
857840}
0 commit comments