Skip to content

feat(mock): Add support for bytes and unsigned integer field types #31

@SebastienMelki

Description

@SebastienMelki

Feature Description

The mock generator needs to handle several missing protobuf field types including bytes fields and all unsigned integer variants (uint32, uint64, fixed32, fixed64, etc.).

Current State

These field types currently generate TODO comments:

  • protoreflect.BytesKind
  • protoreflect.Uint32Kind, protoreflect.Fixed32Kind
  • protoreflect.Uint64Kind, protoreflect.Fixed64Kind
  • protoreflect.Sint32Kind, protoreflect.Sfixed32Kind
  • protoreflect.Sint64Kind, protoreflect.Sfixed64Kind

Proposed Solution

1. Bytes Field Support

case protoreflect.BytesKind:
    if field.Desc.IsList() {
        gf.P(varName, ".", fieldName, " = [][]byte{")
        for i := 0; i < defaultListSize; i++ {
            gf.P("selectBytesExample(\"", fieldPath, "\", generateBytes),")
        }
        gf.P("}")
    } else {
        gf.P(varName, ".", fieldName, " = selectBytesExample(\"", fieldPath, "\", generateBytes)")
    }

2. Unsigned Integer Support

case protoreflect.Uint32Kind, protoreflect.Fixed32Kind:
    if field.Desc.IsList() {
        gf.P(varName, ".", fieldName, " = []uint32{")
        for i := 0; i < defaultListSize; i++ {
            gf.P("uint32(selectUintExample(\"", fieldPath, "\", 42)),")
        }
        gf.P("}")
    } else {
        gf.P(varName, ".", fieldName, " = uint32(selectUintExample(\"", fieldPath, "\", 42))")
    }

case protoreflect.Uint64Kind, protoreflect.Fixed64Kind:
    if field.Desc.IsList() {
        gf.P(varName, ".", fieldName, " = []uint64{")
        for i := 0; i < defaultListSize; i++ {
            gf.P("selectUintExample(\"", fieldPath, "\", 42),")
        }
        gf.P("}")
    } else {
        gf.P(varName, ".", fieldName, " = selectUintExample(\"", fieldPath, "\", 42)")
    }

3. Signed Integer Variants

case protoreflect.Sint32Kind, protoreflect.Sfixed32Kind:
    // Similar to Int32Kind but ensures zigzag encoding compatibility
    if field.Desc.IsList() {
        gf.P(varName, ".", fieldName, " = []int32{")
        for i := 0; i < defaultListSize; i++ {
            gf.P("int32(selectIntExample(\"", fieldPath, "\", 42)),")
        }
        gf.P("}")
    } else {
        gf.P(varName, ".", fieldName, " = int32(selectIntExample(\"", fieldPath, "\", 42))")
    }

4. Helper Functions

// selectBytesExample selects bytes from examples or generates default
func selectBytesExample(fieldPath string, defaultGenerator func() []byte) []byte {
    if examples, ok := fieldExamples[fieldPath]; ok && len(examples) > 0 {
        example := examples[rand.Intn(len(examples))]
        // Check if it's base64 encoded
        if decoded, err := base64.StdEncoding.DecodeString(example); err == nil {
            return decoded
        }
        return []byte(example)
    }
    return defaultGenerator()
}

// selectUintExample selects unsigned integer from examples
func selectUintExample(fieldPath string, defaultValue uint64) uint64 {
    if examples, ok := fieldExamples[fieldPath]; ok && len(examples) > 0 {
        example := examples[rand.Intn(len(examples))]
        if v, err := strconv.ParseUint(example, 10, 64); err == nil {
            return v
        }
    }
    return defaultValue
}

// generateBytes generates random bytes for testing
func generateBytes() []byte {
    size := rand.Intn(32) + 8 // 8-40 bytes
    b := make([]byte, size)
    rand.Read(b)
    return b
}

5. Smart Bytes Generation

For specific field patterns, generate appropriate byte content:

func (g *Generator) getBytesGenerator(field *protogen.Field) string {
    fieldName := strings.ToLower(string(field.Desc.Name()))
    
    switch {
    case strings.Contains(fieldName, "hash"):
        return "generateHash"  // 32-byte hash
    case strings.Contains(fieldName, "signature"):
        return "generateSignature"  // 64-byte signature
    case strings.Contains(fieldName, "key"):
        return "generateKey"  // 16/32-byte key
    case strings.Contains(fieldName, "token"):
        return "generateToken"  // Variable token
    default:
        return "generateBytes"
    }
}

Testing Requirements

  • Proto files with all unsigned integer types
  • Proto files with bytes fields (single and repeated)
  • Test edge cases (max values, zero values)
  • Verify proper type casting in generated code
  • Test bytes fields with various encodings

Files Affected

  • internal/httpgen/mock_generator.go - Add missing type cases
  • internal/httpgen/mock_generator.go - Add helper functions
  • internal/httpgen/testdata/proto/*.proto - Add comprehensive type tests
  • internal/httpgen/testdata/golden/*_http_mock.pb.go - Update golden files

Success Criteria

  • All unsigned integer types generate correct Go types
  • Bytes fields generate valid []byte or [][]byte
  • Type casting is correct for all variants
  • Generated code compiles without warnings
  • Smart generation for common byte field patterns

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions