You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							143 lines
						
					
					
						
							4.7 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							143 lines
						
					
					
						
							4.7 KiB
						
					
					
				| package protocol | |
| 
 | |
| import ( | |
| 	"encoding/binary" | |
| 	"testing" | |
| ) | |
| 
 | |
| // This file demonstrates what FIELD-LEVEL testing would look like | |
| // Currently these tests are NOT run automatically because they require | |
| // complex parsing logic for each API. | |
|  | |
| // TestJoinGroupResponseStructure shows what we SHOULD test but currently don't | |
| func TestJoinGroupResponseStructure(t *testing.T) { | |
| 	t.Skip("This is a demonstration test - shows what we SHOULD check") | |
| 
 | |
| 	// Hypothetical: build a JoinGroup response | |
| 	// response := buildJoinGroupResponseV6(correlationID, generationID, protocolType, ...) | |
|  | |
| 	// What we SHOULD verify: | |
| 	t.Log("Field-level checks we should perform:") | |
| 	t.Log("  1. Error code (int16) - always present") | |
| 	t.Log("  2. Generation ID (int32) - always present") | |
| 	t.Log("  3. Protocol type (string/compact string) - nullable in some versions") | |
| 	t.Log("  4. Protocol name (string/compact string) - always present") | |
| 	t.Log("  5. Leader (string/compact string) - always present") | |
| 	t.Log("  6. Member ID (string/compact string) - always present") | |
| 	t.Log("  7. Members array - NON-NULLABLE, can be empty but must exist") | |
| 	t.Log("     ^-- THIS is where the current bug is!") | |
| 
 | |
| 	// Example of what parsing would look like: | |
| 	// offset := 0 | |
| 	// errorCode := binary.BigEndian.Uint16(response[offset:]) | |
| 	// offset += 2 | |
| 	// generationID := binary.BigEndian.Uint32(response[offset:]) | |
| 	// offset += 4 | |
| 	// ... parse protocol type ... | |
| 	// ... parse protocol name ... | |
| 	// ... parse leader ... | |
| 	// ... parse member ID ... | |
| 	// membersLength := parseCompactArray(response[offset:]) | |
| 	// if membersLength < 0 { | |
| 	//     t.Error("Members array is null, but it should be non-nullable!") | |
| 	// } | |
| } | |
| 
 | |
| // TestProduceResponseStructure shows another example | |
| func TestProduceResponseStructure(t *testing.T) { | |
| 	t.Skip("This is a demonstration test - shows what we SHOULD check") | |
| 
 | |
| 	t.Log("Produce response v7 structure:") | |
| 	t.Log("  1. Topics array - must not be null") | |
| 	t.Log("     - Topic name (string)") | |
| 	t.Log("     - Partitions array - must not be null") | |
| 	t.Log("       - Partition ID (int32)") | |
| 	t.Log("       - Error code (int16)") | |
| 	t.Log("       - Base offset (int64)") | |
| 	t.Log("       - Log append time (int64)") | |
| 	t.Log("       - Log start offset (int64)") | |
| 	t.Log("  2. Throttle time (int32) - v1+") | |
| } | |
| 
 | |
| // CompareWithReferenceImplementation shows ideal testing approach | |
| func TestCompareWithReferenceImplementation(t *testing.T) { | |
| 	t.Skip("This would require a reference Kafka broker or client library") | |
| 
 | |
| 	// Ideal approach: | |
| 	t.Log("1. Generate test data") | |
| 	t.Log("2. Build response with our Gateway") | |
| 	t.Log("3. Build response with kafka-go or Sarama library") | |
| 	t.Log("4. Compare byte-by-byte") | |
| 	t.Log("5. If different, highlight which fields differ") | |
| 
 | |
| 	// This would catch: | |
| 	// - Wrong field order | |
| 	// - Wrong field encoding | |
| 	// - Missing fields | |
| 	// - Null vs empty distinctions | |
| } | |
| 
 | |
| // CurrentTestingApproach documents what we actually do | |
| func TestCurrentTestingApproach(t *testing.T) { | |
| 	t.Log("Current testing strategy (as of Oct 2025):") | |
| 	t.Log("") | |
| 	t.Log("LEVEL 1: Static Code Analysis") | |
| 	t.Log("  Tool: check_responses.sh") | |
| 	t.Log("  Checks: Correlation ID patterns") | |
| 	t.Log("  Coverage: Good for known issues") | |
| 	t.Log("") | |
| 	t.Log("LEVEL 2: Protocol Format Tests") | |
| 	t.Log("  Tool: TestFlexibleResponseHeaderFormat") | |
| 	t.Log("  Checks: Flexible vs non-flexible classification") | |
| 	t.Log("  Coverage: Header format only") | |
| 	t.Log("") | |
| 	t.Log("LEVEL 3: Integration Testing") | |
| 	t.Log("  Tool: Schema Registry, kafka-go, Sarama, Java client") | |
| 	t.Log("  Checks: Real client compatibility") | |
| 	t.Log("  Coverage: Complete but requires manual debugging") | |
| 	t.Log("") | |
| 	t.Log("MISSING: Field-level response body validation") | |
| 	t.Log("  This is why JoinGroup issue wasn't caught by unit tests") | |
| } | |
| 
 | |
| // parseCompactArray is a helper that would be needed for field-level testing | |
| func parseCompactArray(data []byte) int { | |
| 	// Compact array encoding: varint length (length+1 for non-null, 0 for null) | |
| 	length := int(data[0]) | |
| 	if length == 0 { | |
| 		return -1 // null | |
| 	} | |
| 	return length - 1 // actual length | |
| } | |
| 
 | |
| // Example of a REAL field-level test we could write | |
| func TestMetadataResponseHasBrokers(t *testing.T) { | |
| 	t.Skip("Example of what a real field-level test would look like") | |
| 
 | |
| 	// Build a minimal metadata response | |
| 	response := make([]byte, 0, 256) | |
| 
 | |
| 	// Brokers array (non-nullable) | |
| 	brokerCount := uint32(1) | |
| 	response = append(response, | |
| 		byte(brokerCount>>24), | |
| 		byte(brokerCount>>16), | |
| 		byte(brokerCount>>8), | |
| 		byte(brokerCount)) | |
| 
 | |
| 	// Broker 1 | |
| 	response = append(response, 0, 0, 0, 1) // node_id = 1 | |
| 	// ... more fields ... | |
|  | |
| 	// Parse it back | |
| 	offset := 0 | |
| 	parsedCount := binary.BigEndian.Uint32(response[offset : offset+4]) | |
| 
 | |
| 	// Verify | |
| 	if parsedCount == 0 { | |
| 		t.Error("Metadata response has 0 brokers - should have at least 1") | |
| 	} | |
| 
 | |
| 	t.Logf("✓ Metadata response correctly has %d broker(s)", parsedCount) | |
| } | |
| 
 |