From 33180c9f55df85dd302091c0bc924b01e995b79f Mon Sep 17 00:00:00 2001 From: Kevin Dew Date: Mon, 8 Mar 2021 13:57:37 +0000 Subject: [PATCH 1/2] Generate unique items for arrays with the "uniqueItems" property This re-calculates the values of an array if the "uniqueItems" property is set and a non-unique item has been generated. This has been to help resolve flaky tests that expect a random array and occasionally fail due to the generation of an array with collisions. As there is a risk that this can produce an eternal loop I've put in a check that if there are very number of attempts to generate the unique value then it should abort. This does produce a risk of failure if there is a low amount of variance in the allowed variables. This is to resolve issues such as: ``` GovukSchemas::InvalidContentGenerated: An invalid content item was generated. This probably means there's a bug in the generator that causes it to output invalid values. Below you'll find the generated payload, the validation errors and the schema that was used. ... Validation errors: -------------------------- [ { "schema": "869a7d30-f94e-5044-84e7-dc3c01c8f1ba#", "fragment": "#/details/featured_attachments", "message": "The property '#/details/featured_attachments' contained duplicated array values in schema 869a7d30-f94e-5044-84e7-dc3c01c8f1ba#", "failed_attribute": "UniqueItems" }, { "schema": "869a7d30-f94e-5044-84e7-dc3c01c8f1ba", "fragment": "#/details/featured_attachments", "message": "The property '#/details/featured_attachments' contained duplicated array values in schema 869a7d30-f94e-5044-84e7-dc3c01c8f1ba", "failed_attribute": "UniqueItems" } ] ``` --- lib/govuk_schemas/random_schema_generator.rb | 19 +++++++++- spec/lib/random_schema_generator_spec.rb | 39 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/lib/govuk_schemas/random_schema_generator.rb b/lib/govuk_schemas/random_schema_generator.rb index 0b6381d..6251a4f 100644 --- a/lib/govuk_schemas/random_schema_generator.rb +++ b/lib/govuk_schemas/random_schema_generator.rb @@ -116,12 +116,27 @@ def generate_random_object(subschema) def generate_random_array(props) min = props["minItems"] || 0 max = props["maxItems"] || 10 + unique = props["uniqueItems"] == true num_items = @random.rand(min..max) + items = [] + attempts = 0 + max_attempts = num_items * 100 - num_items.times.map do + until items.length == num_items # sometimes arrays don't have `items` specified, not sure if this is a bug - generate_value(props["items"] || {}) + new_value = generate_value(props["items"] || {}) + + if unique && items.include?(new_value) + attempts += 1 + raise "Failed to create a unique array item after #{max_attempts} attempts" if attempts >= max_attempts + next + end + + attempts = 0 + items << new_value end + + items end def generate_random_string(props) diff --git a/spec/lib/random_schema_generator_spec.rb b/spec/lib/random_schema_generator_spec.rb index 82f0c98..6ff3f45 100644 --- a/spec/lib/random_schema_generator_spec.rb +++ b/spec/lib/random_schema_generator_spec.rb @@ -110,5 +110,44 @@ expect(generator.payload["my_enum"]).to eq("a") expect(generator.payload.keys).to include("my_field") end + + describe "unique arrays" do + it "handles arrays that require unique items" do + schema = { + "type" => "array", + "items" => { "type" => "string" }, + "uniqueItems" => true, + "minItems" => 5, + "maxItems" => 5, + } + + generator = GovukSchemas::RandomSchemaGenerator.new(schema: schema) + + # These stubs are to ensure determinism in the random array value + # generation. + allow(generator).to receive(:generate_value).and_call_original + allow(generator) + .to receive(:generate_value) + .with({ "type" => "string" }) + .and_return(*%w[a a b b c c d d e e]) + + expect(generator.payload).to match_array(%w[a b c d e]) + end + + it "raises an error for a situation where it can't generate a random array" do + schema = { + "type" => "array", + "items" => { "enum" => %w[a b] }, + "uniqueItems" => true, + "minItems" => 3, + "maxItems" => 3, + } + + generator = GovukSchemas::RandomSchemaGenerator.new(schema: schema) + + expect { generator.payload } + .to raise_error "Failed to create a unique array item after 300 attempts" + end + end end end From 0d4216707a8c8aaa913765d08896bfa7b553997a Mon Sep 17 00:00:00 2001 From: Kevin Dew Date: Mon, 8 Mar 2021 14:33:26 +0000 Subject: [PATCH 2/2] Bump version to 4.3.0 --- CHANGELOG.md | 4 ++++ lib/govuk_schemas/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 497adfc..199a0ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 4.3.0 + +* Generate unique items for arrays with the "uniqueItems" property. ([#63](https://github.com/alphagov/govuk_schemas/pull/63)) + # 4.2.0 * Add support for generating random HH:MM time strings that match a regex. ([#62](https://github.com/alphagov/govuk_schemas/pull/62)) diff --git a/lib/govuk_schemas/version.rb b/lib/govuk_schemas/version.rb index 22ce071..f4df86a 100644 --- a/lib/govuk_schemas/version.rb +++ b/lib/govuk_schemas/version.rb @@ -1,4 +1,4 @@ module GovukSchemas # @private - VERSION = "4.2.0".freeze + VERSION = "4.3.0".freeze end