add duration test
This commit is contained in:
parent
202de3a05e
commit
11f1b9eacd
|
|
@ -1,87 +0,0 @@
|
|||
package options
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Common", func() {
|
||||
Context("Duration", func() {
|
||||
type marshalJSONTableInput struct {
|
||||
duration Duration
|
||||
expectedJSON string
|
||||
}
|
||||
|
||||
DescribeTable("MarshalJSON",
|
||||
func(in marshalJSONTableInput) {
|
||||
data, err := in.duration.MarshalJSON()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(data)).To(Equal(in.expectedJSON))
|
||||
|
||||
var d Duration
|
||||
Expect(json.Unmarshal(data, &d)).To(Succeed())
|
||||
Expect(d).To(Equal(in.duration))
|
||||
},
|
||||
Entry("30 seconds", marshalJSONTableInput{
|
||||
duration: Duration(30 * time.Second),
|
||||
expectedJSON: "\"30s\"",
|
||||
}),
|
||||
Entry("1 minute", marshalJSONTableInput{
|
||||
duration: Duration(1 * time.Minute),
|
||||
expectedJSON: "\"1m0s\"",
|
||||
}),
|
||||
Entry("1 hour 15 minutes", marshalJSONTableInput{
|
||||
duration: Duration(75 * time.Minute),
|
||||
expectedJSON: "\"1h15m0s\"",
|
||||
}),
|
||||
Entry("A zero Duration", marshalJSONTableInput{
|
||||
duration: Duration(0),
|
||||
expectedJSON: "\"0s\"",
|
||||
}),
|
||||
)
|
||||
|
||||
type unmarshalJSONTableInput struct {
|
||||
json string
|
||||
expectedErr error
|
||||
expectedDuration Duration
|
||||
}
|
||||
|
||||
DescribeTable("UnmarshalJSON",
|
||||
func(in unmarshalJSONTableInput) {
|
||||
// A duration must be initialised pointer before UnmarshalJSON will work.
|
||||
zero := Duration(0)
|
||||
d := &zero
|
||||
|
||||
err := d.UnmarshalJSON([]byte(in.json))
|
||||
if in.expectedErr != nil {
|
||||
Expect(err).To(MatchError(in.expectedErr.Error()))
|
||||
} else {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
Expect(d).ToNot(BeNil())
|
||||
Expect(*d).To(Equal(in.expectedDuration))
|
||||
},
|
||||
Entry("1m", unmarshalJSONTableInput{
|
||||
json: "\"1m\"",
|
||||
expectedDuration: Duration(1 * time.Minute),
|
||||
}),
|
||||
Entry("30s", unmarshalJSONTableInput{
|
||||
json: "\"30s\"",
|
||||
expectedDuration: Duration(30 * time.Second),
|
||||
}),
|
||||
Entry("1h15m", unmarshalJSONTableInput{
|
||||
json: "\"1h15m\"",
|
||||
expectedDuration: Duration(75 * time.Minute),
|
||||
}),
|
||||
Entry("am", unmarshalJSONTableInput{
|
||||
json: "\"am\"",
|
||||
expectedErr: errors.New("time: invalid duration \"am\""),
|
||||
expectedDuration: Duration(0),
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
package options
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
type result struct {
|
||||
Duration time.Duration `json:"duration"`
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input map[string]interface{}
|
||||
out result
|
||||
expected time.Duration
|
||||
expectedErr bool
|
||||
}{
|
||||
{
|
||||
name: "Valid String Duration with single unit",
|
||||
input: map[string]interface{}{"duration": "3s"},
|
||||
out: result{},
|
||||
expected: 3 * time.Second,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid String Duration with multiple units",
|
||||
input: map[string]interface{}{"duration": "1h20m30s"},
|
||||
out: result{},
|
||||
expected: 1*time.Hour + 20*time.Minute + 30*time.Second,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid Float Duration",
|
||||
input: map[string]interface{}{"duration": 2.5},
|
||||
out: result{},
|
||||
expected: 2500 * time.Millisecond,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "Valid Int64 Duration",
|
||||
input: map[string]interface{}{"duration": int64(5000000000)},
|
||||
out: result{},
|
||||
expected: 5 * time.Second,
|
||||
expectedErr: false,
|
||||
},
|
||||
{
|
||||
name: "Invalid String",
|
||||
input: map[string]interface{}{"duration": "invalid"},
|
||||
out: result{},
|
||||
expected: 0,
|
||||
expectedErr: true,
|
||||
},
|
||||
{
|
||||
name: "Unsupported Type",
|
||||
input: map[string]interface{}{"duration": true},
|
||||
out: result{},
|
||||
expected: 0,
|
||||
expectedErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var result struct {
|
||||
Duration time.Duration `json:"duration"`
|
||||
}
|
||||
|
||||
err := Decode(tt.input, &result)
|
||||
if (err != nil) != tt.expectedErr {
|
||||
t.Errorf("expected error: %v, got: %v", tt.expectedErr, err)
|
||||
}
|
||||
|
||||
if !tt.expectedErr {
|
||||
if result.Duration != tt.expected {
|
||||
t.Errorf("expected: %v, got: %v", tt.expected, result.Duration)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -69,16 +69,25 @@ func LoadYAML(configFileName string, opts interface{}) error {
|
|||
return fmt.Errorf("error unmarshalling config: %w", err)
|
||||
}
|
||||
|
||||
return Decode(intermediate, opts)
|
||||
}
|
||||
|
||||
func Decode(input interface{}, result interface{}) error {
|
||||
// Using mapstructure to decode arbitrary yaml structure into options and
|
||||
// merge with existing values instead of overwriting everything. This is especially
|
||||
// important as we have a lot of default values for boolean which are supposed to be
|
||||
// true by default. Normally by just parsing through yaml all booleans that aren't
|
||||
// referenced in the config file would be parsed as false and we cannot identify after
|
||||
// the fact if they have been explicitly set to false or have not been referenced.
|
||||
return Decode(intermediate, opts)
|
||||
}
|
||||
|
||||
// Decode processes an input map and decodes it into a given struct while preserving default values.
|
||||
// It ensures proper conversion of duration values from strings, floats, and int64 into time.Duration.
|
||||
//
|
||||
// Parameters:
|
||||
// - input: A map[string]interface{} representing the input data.
|
||||
// - result: A pointer to a struct where the decoded values will be stored.
|
||||
//
|
||||
// Returns:
|
||||
// - An error if decoding fails or if there are unmapped keys.
|
||||
func Decode(input interface{}, result interface{}) error {
|
||||
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(toDurationHookFunc()),
|
||||
Metadata: nil, // Don't track any metadata
|
||||
|
|
|
|||
Loading…
Reference in New Issue