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