fix: continued moving everything to orderedmaps plus cleaned up most the tests

This commit is contained in:
Tristan Cartledge
2023-12-01 17:37:07 +00:00
parent 0f3d0cb28f
commit a4ad09aab3
169 changed files with 3435 additions and 3764 deletions

View File

@@ -13,9 +13,11 @@ import (
"gopkg.in/yaml.v3"
)
const Example = "Example"
const Examples = "Examples"
const Schema = "Schema"
const (
Example = "Example"
Examples = "Examples"
Schema = "Schema"
)
type MockType int
@@ -27,7 +29,7 @@ const (
// MockGenerator is used to generate mocks for high-level mockable structs or *base.Schema pointers.
// The mock generator will attempt to generate a mock from a struct using the following fields:
// - Example: any type, this is the default example to use if no examples are present.
// - Examples: orderedmap.Map[string, *base.Example], this is a map of examples keyed by name.
// - Examples: *orderedmap.Map[string, *base.Example], this is a map of examples keyed by name.
// - Schema: *base.SchemaProxy, this is the schema to use if no examples are present.
//
// The mock generator will attempt to generate a mock from a *base.Schema pointer.
@@ -61,7 +63,7 @@ func (mg *MockGenerator) SetPretty() {
// GenerateMock generates a mock for a given high-level mockable struct. The mockable struct must contain the following fields:
// Example: any type, this is the default example to use if no examples are present.
// Examples: orderedmap.Map[string, *base.Example], this is a map of examples keyed by name.
// Examples: *orderedmap.Map[string, *base.Example], this is a map of examples keyed by name.
// Schema: *base.SchemaProxy, this is the schema to use if no examples are present.
// The name parameter is optional, if provided, the mock generator will attempt to find an example with the given name.
// If no name is provided, the first example will be used.
@@ -89,10 +91,21 @@ func (mg *MockGenerator) GenerateMock(mock any, name string) ([]byte, error) {
}
// if the value has an example, try and render it out as is.
exampleValue := v.FieldByName(Example).Interface()
if exampleValue != nil {
// try and serialize the example value
return mg.renderMock(exampleValue), nil
f := v.FieldByName(Example)
if !f.IsNil() {
// Pointer/Interface Shenanigans
ex := f.Interface()
if y, ok := ex.(*yaml.Node); ok {
if y != nil {
ex = y
} else {
ex = nil
}
}
if ex != nil {
// try and serialize the example value
return mg.renderMock(ex), nil
}
}
// if there is no example, but there are multi-examples.
@@ -100,8 +113,8 @@ func (mg *MockGenerator) GenerateMock(mock any, name string) ([]byte, error) {
examplesValue := examples.Interface()
if examplesValue != nil && !examples.IsNil() {
// cast examples to orderedmap.Map[string, any]
examplesMap := examplesValue.(orderedmap.Map[string, *highbase.Example])
// cast examples to *orderedmap.Map[string, *highbase.Example]
examplesMap := examplesValue.(*orderedmap.Map[string, *highbase.Example])
// if the name is not empty, try and find the example by name
for pair := orderedmap.First(examplesMap); pair != nil; pair = pair.Next() {
@@ -157,6 +170,11 @@ func (mg *MockGenerator) renderMock(v any) []byte {
func (mg *MockGenerator) renderMockJSON(v any) []byte {
var data []byte
if y, ok := v.(*yaml.Node); ok {
_ = y.Decode(&v)
}
// determine the type, render properly.
switch reflect.ValueOf(v).Kind() {
case reflect.Map, reflect.Slice, reflect.Array, reflect.Struct, reflect.Ptr:

View File

@@ -13,6 +13,7 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
lowbase "github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/pb33f/libopenapi/orderedmap"
"github.com/pb33f/libopenapi/utils"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
)
@@ -20,13 +21,13 @@ import (
type fakeMockable struct {
Schema *highbase.SchemaProxy
Example any
Examples orderedmap.Map[string, *highbase.Example]
Examples *orderedmap.Map[string, *highbase.Example]
}
type fakeMockableButWithASchemaNotAProxy struct {
Schema *highbase.Schema
Example any
Examples orderedmap.Map[string, *highbase.Example]
Examples *orderedmap.Map[string, *highbase.Example]
}
var simpleFakeMockSchema = `type: string
@@ -55,7 +56,7 @@ func createFakeMock(mock string, values map[string]any, example any) *fakeMockab
for k, v := range values {
examples.Set(k, &highbase.Example{
Value: v,
Value: utils.CreateYamlNode(v),
})
}
return &fakeMockable{
@@ -78,7 +79,7 @@ func createFakeMockWithoutProxy(mock string, values map[string]any, example any)
for k, v := range values {
examples.Set(k, &highbase.Example{
Value: v,
Value: utils.CreateYamlNode(v),
})
}
return &fakeMockableButWithASchemaNotAProxy{
@@ -110,7 +111,6 @@ func TestMockGenerator_GenerateJSONMock_BadObject(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_EmptyObject(t *testing.T) {
mg := NewMockGenerator(JSON)
mock, err := mg.GenerateMock(&fakeMockable{}, "")
assert.NoError(t, err)
@@ -118,7 +118,6 @@ func TestMockGenerator_GenerateJSONMock_EmptyObject(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_SuppliedExample_JSON(t *testing.T) {
fakeExample := map[string]any{
"fish-and-chips": "cod-and-chips-twice",
}
@@ -130,7 +129,6 @@ func TestMockGenerator_GenerateJSONMock_SuppliedExample_JSON(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_SuppliedExample_YAML(t *testing.T) {
fakeExample := map[string]any{
"fish-and-chips": "cod-and-chips-twice",
}
@@ -208,7 +206,6 @@ func TestMockGenerator_GenerateJSONMock_MultiExamples_YAML(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_NoExamples_JSON(t *testing.T) {
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
mg := NewMockGenerator(JSON)
mock, err := mg.GenerateMock(fake, "")
@@ -217,16 +214,14 @@ func TestMockGenerator_GenerateJSONMock_NoExamples_JSON(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_NoExamples_YAML(t *testing.T) {
fake := createFakeMock(simpleFakeMockSchema, nil, nil)
mg := NewMockGenerator(YAML)
mock, err := mg.GenerateMock(fake, "")
assert.NoError(t, err)
assert.Equal(t, "magic-herbs", string(mock))
assert.Equal(t, "magic-herbs", strings.TrimSpace(string(mock)))
}
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_JSON(t *testing.T) {
fake := createFakeMock(objectFakeMockSchema, nil, nil)
mg := NewMockGenerator(JSON)
mock, err := mg.GenerateMock(fake, "")
@@ -244,7 +239,6 @@ func TestMockGenerator_GenerateJSONMock_Object_NoExamples_JSON(t *testing.T) {
}
func TestMockGenerator_GenerateJSONMock_Object_NoExamples_YAML(t *testing.T) {
fake := createFakeMock(objectFakeMockSchema, nil, nil)
mg := NewMockGenerator(YAML)
mock, err := mg.GenerateMock(fake, "")
@@ -263,7 +257,6 @@ func TestMockGenerator_GenerateJSONMock_Object_NoExamples_YAML(t *testing.T) {
// should result in the exact same output as the above test
func TestMockGenerator_GenerateJSONMock_Object_RawSchema(t *testing.T) {
fake := createFakeMockWithoutProxy(objectFakeMockSchema, nil, nil)
mg := NewMockGenerator(YAML)

View File

@@ -19,33 +19,35 @@ import (
"golang.org/x/exp/slices"
)
const rootType = "rootType"
const stringType = "string"
const numberType = "number"
const integerType = "integer"
const booleanType = "boolean"
const objectType = "object"
const arrayType = "array"
const int32Type = "int32"
const floatType = "float"
const doubleType = "double"
const byteType = "byte"
const binaryType = "binary"
const passwordType = "password"
const dateType = "date"
const dateTimeType = "date-time"
const timeType = "time"
const emailType = "email"
const hostnameType = "hostname"
const ipv4Type = "ipv4"
const ipv6Type = "ipv6"
const uriType = "uri"
const uriReferenceType = "uri-reference"
const uuidType = "uuid"
const allOfType = "allOf"
const anyOfType = "anyOf"
const oneOfType = "oneOf"
const itemsType = "items"
const (
rootType = "rootType"
stringType = "string"
numberType = "number"
integerType = "integer"
booleanType = "boolean"
objectType = "object"
arrayType = "array"
int32Type = "int32"
floatType = "float"
doubleType = "double"
byteType = "byte"
binaryType = "binary"
passwordType = "password"
dateType = "date"
dateTimeType = "date-time"
timeType = "time"
emailType = "email"
hostnameType = "hostname"
ipv4Type = "ipv4"
ipv6Type = "ipv6"
uriType = "uri"
uriReferenceType = "uri-reference"
uuidType = "uuid"
allOfType = "allOf"
anyOfType = "anyOf"
oneOfType = "oneOf"
itemsType = "items"
)
// used to generate random words if there is no dictionary applied.
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -97,10 +99,12 @@ func (wr *SchemaRenderer) DisableRequiredCheck() {
// DiveIntoSchema will dive into a schema and inject values from examples into a map. If there are no examples in
// the schema, then the renderer will attempt to generate a value based on the schema type, format and pattern.
func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, structure map[string]any, depth int) {
// got an example? use it, we're done here.
if schema.Example != nil {
structure[key] = schema.Example
var example any
_ = schema.Example.Decode(&example)
structure[key] = example
return
}
@@ -114,7 +118,12 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
if slices.Contains(schema.Type, stringType) {
// check for an enum, if there is one, then pick a random value from it.
if schema.Enum != nil && len(schema.Enum) > 0 {
structure[key] = schema.Enum[rand.Int()%len(schema.Enum)]
enum := schema.Enum[rand.Int()%len(schema.Enum)]
var example any
_ = enum.Decode(&example)
structure[key] = example
} else {
// generate a random value based on the schema format, pattern and length values.
@@ -187,7 +196,12 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
if slices.Contains(schema.Type, numberType) || slices.Contains(schema.Type, integerType) {
if schema.Enum != nil && len(schema.Enum) > 0 {
structure[key] = schema.Enum[rand.Int()%len(schema.Enum)]
enum := schema.Enum[rand.Int()%len(schema.Enum)]
var example any
_ = enum.Decode(&example)
structure[key] = example
} else {
var minimum int64 = 1
@@ -303,7 +317,6 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
// an array needs an items schema
itemsSchema := schema.Items
if itemsSchema != nil {
// otherwise the items value is a schema, so we need to dive into it
if itemsSchema.IsA() {
@@ -326,7 +339,6 @@ func (wr *SchemaRenderer) DiveIntoSchema(schema *base.Schema, key string, struct
}
}
}
}
func readFile(file io.Reader) []string {
@@ -350,7 +362,6 @@ func ReadDictionary(dictionaryLocation string) []string {
// to prevent a stack overflow, the maximum depth is 100 (anything more than this is probably a bug).
// set the values to 0 to return the first word returned, essentially ignore the min and max values.
func (wr *SchemaRenderer) RandomWord(min, max int64, depth int) string {
// break out if we've gone too deep
if depth > 100 {
return fmt.Sprintf("no-word-found-%d-%d", min, max)

View File

@@ -38,7 +38,6 @@ properties:
}
func createSchemaRenderer() *SchemaRenderer {
osDict := "/usr/share/dict/words"
if _, err := os.Stat(osDict); err != nil {
osDict = ""
@@ -68,7 +67,6 @@ func getSchema(schema []byte) *highbase.Schema {
}
func TestRenderExample_StringWithExample(t *testing.T) {
testObject := `type: string
example: dog`
@@ -79,11 +77,9 @@ example: dog`
wr.DiveIntoSchema(compiled, "pb33f", journeyMap, 0)
assert.Equal(t, journeyMap["pb33f"], "dog")
}
func TestRenderExample_StringWithNoExample(t *testing.T) {
testObject := `type: string`
compiled := getSchema([]byte(testObject))
@@ -95,11 +91,9 @@ func TestRenderExample_StringWithNoExample(t *testing.T) {
assert.NotNil(t, journeyMap["pb33f"])
assert.GreaterOrEqual(t, len(journeyMap["pb33f"].(string)), 3)
assert.LessOrEqual(t, len(journeyMap["pb33f"].(string)), 10)
}
func TestRenderExample_StringWithNoExample_Format_Datetime(t *testing.T) {
testObject := `type: string
format: date-time`
@@ -115,7 +109,6 @@ format: date-time`
}
func TestRenderExample_StringWithNoExample_Pattern_Email(t *testing.T) {
testObject := `type: string
pattern: "^[a-z]{5,10}@[a-z]{5,10}\\.(com|net|org)$"` // an email address
@@ -134,7 +127,6 @@ pattern: "^[a-z]{5,10}@[a-z]{5,10}\\.(com|net|org)$"` // an email address
}
func TestRenderExample_StringWithNoExample_Pattern_PhoneNumber(t *testing.T) {
testObject := `type: string
pattern: "^\\([0-9]{3}\\)-[0-9]{3}-[0-9]{4}$"` // a phone number
@@ -152,7 +144,6 @@ pattern: "^\\([0-9]{3}\\)-[0-9]{3}-[0-9]{4}$"` // a phone number
}
func TestRenderExample_StringWithNoExample_Format_Date(t *testing.T) {
testObject := `type: string
format: date`
@@ -390,7 +381,6 @@ minLength: 3`
assert.NotNil(t, journeyMap["pb33f"])
assert.LessOrEqual(t, len(journeyMap["pb33f"].(string)), 8)
assert.GreaterOrEqual(t, len(journeyMap["pb33f"].(string)), 3)
}
func TestRenderExample_NumberNoExample_Default(t *testing.T) {
@@ -771,7 +761,6 @@ properties:
assert.Equal(t, journeyMap["pb33f"].(map[string]interface{})["price"].(float64), 19.99)
assert.Equal(t, journeyMap["pb33f"].(map[string]interface{})["category"].(string), "shirts")
assert.Equal(t, journeyMap["pb33f"].(map[string]interface{})["image"].(string), "https://pb33f.io/images/t-shirt.png")
}
func TestRenderExample_TestGiftshopProduct_UsingTopLevelExample(t *testing.T) {
@@ -839,7 +828,6 @@ example:
assert.Equal(t, journeyMap["pb33f"].(map[string]interface{})["category"].(string), "not-a-category")
assert.Equal(t, journeyMap["pb33f"].(map[string]interface{})["image"].(string), "not-an-image")
}
func TestRenderExample_TestGiftshopProduct_UsingNoExamples(t *testing.T) {
@@ -889,7 +877,6 @@ properties:
assert.NotEmpty(t, journeyMap["pb33f"].(map[string]interface{})["price"].(float32))
assert.NotEmpty(t, journeyMap["pb33f"].(map[string]interface{})["category"].(string))
assert.NotEmpty(t, journeyMap["pb33f"].(map[string]interface{})["image"].(string))
}
func TestRenderExample_Test_MultiPolymorphic(t *testing.T) {
@@ -931,7 +918,7 @@ properties:
burger := journeyMap["pb33f"].(map[string]interface{})["burger"].(map[string]interface{})
assert.NotNil(t, burger)
assert.NotEmpty(t, burger["name"].(string))
assert.NotZero(t, burger["weight"].(int64))
assert.NotZero(t, burger["weight"].(int))
assert.NotEmpty(t, burger["patty"].(string))
assert.True(t, burger["frozen"].(bool))
}
@@ -1083,7 +1070,6 @@ func (errReader) Read(p []byte) (n int, err error) {
}
func TestReadDictionary_BadReader(t *testing.T) {
words := readFile(errReader(0))
assert.LessOrEqual(t, len(words), 0)
}
@@ -1113,14 +1099,13 @@ func TestWordRenderer_RandomWordMinMaxZero(t *testing.T) {
}
func TestRenderSchema_NestedDeep(t *testing.T) {
deepNest := createNestedStructure()
journeyMap := make(map[string]any)
wr := createSchemaRenderer()
wr.DiveIntoSchema(deepNest.Schema(), "pb33f", journeyMap, 0)
assert.NotNil(t, journeyMap["pb33f"])
var journeyLevel = 0
journeyLevel := 0
var dive func(mapNode map[string]any, level int)
// count the levels to validate the recursion hard limit.
dive = func(mapNode map[string]any, level int) {
@@ -1144,7 +1129,6 @@ func TestCreateRendererUsingDictionary(t *testing.T) {
}
func createNestedStructure() *highbase.SchemaProxy {
schema := `type: [object]
properties:
name: