pact-foundation / pact-go

Golang version of Pact. Pact is a contract testing framework for HTTP APIs and non-HTTP asynchronous messaging systems.
http://pact.io
MIT License
828 stars 103 forks source link

"example=0" struct tags for numbers lead to wrong example values in contract #409

Open Metamogul opened 2 weeks ago

Metamogul commented 2 weeks ago

Software versions

pact-go@2.0.4

Expected behaviour

If I define a struct like

type exampleRange struct {
  Lowest  float `json:"lowest" pact:"example=0"` 
  Highest float `json:"lowest" pact:"example=100"`  
}

and use it in building an interaction response such as

WillRespondWith(200, func(b *consumer.V2ResponseBuilder) {
  b.BodyMatch(exampleRange)
}).

I'd expect these values to be reflected in the response body in the json pact files like

...
"response": {
  "body": {
    "exampleRange": {
      "highest": 100
      "lowest": 0.0
    }
  }
}
...

Actual behaviour

Instead, the example for the float with the specified example value 0 is set to 1.1:

...
"response": {
  "body": {
    "exampleRange": {
      "highest": 100
      "lowest": 1.1
    }
  }
}
...

This is caused by the following implementation in matchers/matcher.go:328:

...
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
        reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        if params.number.integer != 0 {
            return Like(params.number.integer)
        }
        return Like(1)
    case reflect.Float32, reflect.Float64:
        if params.number.float != 0 {
            return Like(params.number.float)
        }
        return Like(1.1)
...

Obviously the problem affects floats and ints alike. A simple fix seems to be to return Like(0) as default or return Like(0.0) respectively. However I've just discovered the problem and my knowledge of the pact-go codebase is not deep enough to foresee the consequences of this change, so I didn't create PR yet. Could anybody more experienced comment on this?

Steps to reproduce

Use the interaction builder with a response as specified above and observe the generated pact files response body.

mefellows commented 2 weeks ago

Yeah I can't see why we need to even check the value is 0 in the int-like cases. In the case of a floating point number, 0.0 when sent over the wire (i.e. as JSON) is implementation dependent and may get sent as 0 - so we defer to a non-zero number there to preserve the floating point.

I'm open to a PR that allows zero though because ultimately, the user can just update the test to a different number should the downscaling occur.