@@ -40,38 +40,43 @@ type (
4040// }
4141func parseArray (node schemaNode , metadata * parserMetadata ) (Generator , error ) {
4242 // Handle case where contains is set with no minContains (at least 1 must match subschema)
43- minContains := node .MinContains
43+ minContains := util .GetZeroIfNil (node .MinContains , 0 )
44+ maxContains := util .GetZeroIfNil (node .MaxContains , 0 )
45+ minItems := util .GetZeroIfNil (node .MinItems , 0 )
46+ maxItems := util .GetZeroIfNil (node .MaxItems , 0 )
47+ prefixItems := util .GetZeroIfNil (node .PrefixItems , []schemaNode {})
48+
4449 if minContains == 0 && node .Contains != nil {
4550 minContains = 1
4651 }
4752
4853 // Validate Bounds
49- if err := assertLowerUpperBound (node . MinItems , node . MaxItems , "minItems" , "maxItems" ); err != nil {
54+ if err := assertLowerUpperBound (minItems , maxItems , "minItems" , "maxItems" ); err != nil {
5055 return nullGenerator {}, err
5156 }
5257
53- if err := assertLowerUpperBound (node . MinContains , node . MaxContains , "minContains" , "maxContains" ); err != nil {
58+ if err := assertLowerUpperBound (minContains , maxContains , "minContains" , "maxContains" ); err != nil {
5459 return nullGenerator {}, err
5560 }
5661
57- if err := assertLowerUpperBound (node . MinContains , node . MaxItems , "minContains" , "minItems" ); err != nil {
62+ if err := assertLowerUpperBound (minContains , minItems , "minContains" , "minItems" ); err != nil {
5863 return nullGenerator {}, err
5964 }
6065
61- if err := assertLowerUpperBound (len (node . PrefixItems ) + node . MinContains , node . MaxItems , "Tuple items Plus Prefix items. (Note contains does not assume tuple items count towards the total account)" , "minItems" ); err != nil {
66+ if err := assertLowerUpperBound (len (prefixItems ) + minContains , maxItems , "Tuple items Plus Prefix items. (Note contains does not assume tuple items count towards the total account)" , "minItems" ); err != nil {
6267 return nullGenerator {}, err
6368 }
6469
6570 // Validate if tuple makes sense in this context
66- tupleLength := len (node . PrefixItems )
67- if tupleLength > node . MaxItems && node .MaxItems != 0 {
71+ tupleLength := len (prefixItems )
72+ if tupleLength > maxItems && node .MaxItems != nil {
6873 return nullGenerator {}, fmt .Errorf ("tuple length must be less than or equal to maxItems (tupleLength: %d, maxItems: %d)" , tupleLength , node .MaxItems )
6974 }
7075
71- min := util .GetInt (node . MinItems , node . MinContains )
72- max := util .GetInt (node . MaxItems , node . MaxContains )
76+ min := util .GetInt (minItems , minContains )
77+ max := util .GetInt (maxItems , min + defaultOffset )
7378
74- disallowedAdditionalItems := node .Items . DisallowAdditionalItems || (node .AdditionalItems != nil && node .AdditionalItems .IsFalse )
79+ disallowedAdditionalItems := ( node .Items != nil && node . Items . DisallowAdditionalItems ) || (node .AdditionalItems != nil && node .AdditionalItems .IsFalse )
7580
7681 // Force the generator to use only the tuple in the event that additional items
7782 // are not allowed
@@ -116,7 +121,7 @@ func parseArray(node schemaNode, metadata *parserMetadata) (Generator, error) {
116121 UnevaluatedItemsGenerator : unevaluatedItemsGenerator ,
117122 DisallowUnevaluatedItems : disallowUnevaluatedItems ,
118123
119- DisallowAdditional : node .Items .DisallowAdditionalItems ,
124+ DisallowAdditional : node .Items != nil && node . Items .DisallowAdditionalItems ,
120125 AdditionalItemsGenerator : additionalItemGenerator ,
121126
122127 MinContains : minContains ,
@@ -125,19 +130,20 @@ func parseArray(node schemaNode, metadata *parserMetadata) (Generator, error) {
125130 MinItems : min ,
126131 MaxItems : max ,
127132
128- UniqueItems : node .UniqueItems ,
133+ UniqueItems : util . GetZeroIfNil ( node .UniqueItems , false ) ,
129134
130135 schemaNode : node ,
131136 }, nil
132137}
133138
134139func parseTupleGeneratorFromSchemaNode (node schemaNode , metadata * parserMetadata ) ([]Generator , error ) {
135- if len (node .PrefixItems ) != 0 {
136- return parseTupleGenerator (node .PrefixItems , metadata )
140+
141+ if node .PrefixItems != nil && len (* node .PrefixItems ) != 0 {
142+ return parseTupleGenerator (* node .PrefixItems , metadata )
137143 // Legacy support given "items" when passed as an array
138144 // has the same meaning as "prefixItems"
139- } else if len (node .Items .Nodes ) != 0 {
140- return parseTupleGenerator (node .Items .Nodes , metadata )
145+ } else if node . Items != nil && node . Items . Nodes != nil && len (* node .Items .Nodes ) != 0 {
146+ return parseTupleGenerator (* node .Items .Nodes , metadata )
141147 }
142148 return nil , nil
143149}
@@ -171,7 +177,11 @@ func parseAdditionalItems(node schemaNode, metadata *parserMetadata) (Generator,
171177
172178}
173179
174- func parseItemGenerator (additionalData itemsData , metadata * parserMetadata ) (Generator , error ) {
180+ func parseItemGenerator (additionalData * itemsData , metadata * parserMetadata ) (Generator , error ) {
181+ if additionalData == nil {
182+ return nil , nil
183+ }
184+
175185 if additionalData .DisallowAdditionalItems || additionalData .Node == nil {
176186 return nil , nil
177187 }
@@ -189,6 +199,10 @@ func parseItemGeneratorInScope(node schemaNode, metadata *parserMetadata, scope
189199
190200func (g arrayGenerator ) Generate (opts * GeneratorOptions ) interface {} {
191201 opts .overallComplexity ++
202+ if opts .ShouldCutoff () {
203+ return nil
204+ }
205+
192206 tupleLength := len (g .TupleGenerators )
193207 arrayData := make ([]interface {}, 0 )
194208
@@ -227,7 +241,14 @@ func (g arrayGenerator) Generate(opts *GeneratorOptions) interface{} {
227241
228242 // Generate any required "contains" items
229243 for i := 0 ; i < minContains ; i ++ {
230- arrayData = append (arrayData , g .generateConsideringUnique (opts , g .ContainsGenerator , arrayData ))
244+ item , ok := g .generateConsideringUnique (opts , g .ContainsGenerator , arrayData )
245+ if ! ok && i > minContains {
246+ // If we are unable to generate a unique item, and we have already satisfied the minimum contains
247+ // bail out early
248+ break
249+ }
250+
251+ arrayData = append (arrayData , item )
231252 }
232253
233254 if maxItems < minItems {
@@ -240,14 +261,22 @@ func (g arrayGenerator) Generate(opts *GeneratorOptions) interface{} {
240261 itemsToGenerate := opts .Rand .RandomInt (0 , remainingItemsToGenerate )
241262
242263 // Cull the remaining items if the complexity is too high or unevaluated items are not allowed
243- if g .DisallowUnevaluatedItems || opts .MaximumGenerationSteps > 0 && opts . overallComplexity > opts . MaximumGenerationSteps {
264+ if g .DisallowUnevaluatedItems || opts .ShouldMinimize () {
244265 itemsToGenerate = 0
245266 }
246267
247268 // Generate the remaining items up to a random number
248269 // (This might skew the distribution of the length of the array)
249270 for i := 0 ; i < itemsToGenerate || minItems > len (arrayData ); i ++ {
250- arrayData = append (arrayData , g .generateConsideringUnique (opts , itemGen , arrayData ))
271+ item , ok := g .generateConsideringUnique (opts , itemGen , arrayData )
272+
273+ if ! ok && i > minItems {
274+ // If we are unable to generate a unique item, and we have already satisfied the minimum
275+ // bail out early
276+ break
277+ }
278+
279+ arrayData = append (arrayData , item )
251280 }
252281
253282 return arrayData
@@ -263,18 +292,20 @@ func (g arrayGenerator) String() string {
263292}
264293
265294// Will attempt to generate a unique item if the uniqueItems flag is set
266- func (g arrayGenerator ) generateConsideringUnique (opts * GeneratorOptions , itemGenerator Generator , arrayData []interface {}) interface {} {
295+ func (g arrayGenerator ) generateConsideringUnique (opts * GeneratorOptions , itemGenerator Generator , arrayData []interface {}) ( interface {}, bool ) {
267296 if ! g .UniqueItems {
268- return itemGenerator .Generate (opts )
297+ return itemGenerator .Generate (opts ), true
269298 }
270299
300+ currentItems := funk .Map (arrayData , util .MarshalJsonToString ).([]string )
301+
271302 // Generate until we have a unique item
272303 for i := 0 ; i < opts .MaximumUniqueGeneratorAttempts ; i ++ {
273304 item := itemGenerator .Generate (opts )
274- if ! funk .Contains (arrayData , item ) {
275- return item
305+ if ! funk .Contains (currentItems , util . MarshalJsonToString ( item ) ) {
306+ return item , true
276307 }
277308 }
278309
279- return fmt .Sprintf ("Warning: Unable to generate unique item after %d attempts. Recheck passed schema." , opts .MaximumUniqueGeneratorAttempts )
310+ return fmt .Sprintf ("Warning: Unable to generate unique item after %d attempts. Recheck passed schema." , opts .MaximumUniqueGeneratorAttempts ), false
280311}
0 commit comments