Skip to content

Commit

Permalink
Merge pull request #56 from alphagov/seed
Browse files Browse the repository at this point in the history
Make RandomExample/RandomItemGenerator deterministic (use seed)
  • Loading branch information
ChrisBAshton authored Jul 27, 2020
2 parents fbd46f3 + 0ef1679 commit e05adef
Show file tree
Hide file tree
Showing 12 changed files with 180 additions and 156 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# 4.1.0

* Add `seed` parameter to `GovukSchemas::RandomExample` to make the random behaviour deterministic. Given the same seed, the same randomised outputs will be returned ([#56](https://github.com/alphagov/govuk_schemas/pull/56)).

# 4.0.1

* Bump the required Ruby version to >= 2.6.x.
Expand Down
2 changes: 0 additions & 2 deletions govuk_schemas.gemspec
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
# coding: utf-8

lib = File.expand_path("lib", __dir__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require "govuk_schemas/version"
Expand Down
1 change: 0 additions & 1 deletion lib/govuk_schemas.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
require "govuk_schemas/version"
require "govuk_schemas/schema"
require "govuk_schemas/utils"
require "govuk_schemas/random_example"
require "govuk_schemas/document_types"
require "govuk_schemas/example"
Expand Down
101 changes: 0 additions & 101 deletions lib/govuk_schemas/random.rb

This file was deleted.

126 changes: 126 additions & 0 deletions lib/govuk_schemas/random_content_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
module GovukSchemas
# @private
class RandomContentGenerator
WORDS = %w[Lorem ipsum dolor sit amet consectetur adipiscing elit. Ut suscipit at mauris non bibendum. Ut ac massa est. Aenean tempor imperdiet leo vel interdum. Nam sagittis cursus sem ultricies scelerisque. Quisque porttitor risus vel risus finibus eu sollicitudin nisl aliquet. Sed sed lectus ac dolor molestie interdum. Nam molestie pellentesque purus ac vestibulum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Suspendisse non tempor eros. Mauris eu orci hendrerit volutpat lorem in tristique libero. Duis a nibh nibh.].freeze

def initialize(random: Random.new)
@random = random
end

def string_for_type(type)
if type == "date-time"
time
elsif type == "uri"
uri
else
raise "Unknown attribute type `#{type}`"
end
end

def time
arbitrary_time = Time.new(2012, 2, 1)
(arbitrary_time + @random.rand(0..500_000_000)).iso8601
end

# TODO: make this more random with query string, optional anchor.
def uri
"http://example.com#{base_path}#{anchor}"
end

def base_path
"/" + @random.rand(1..5).times.map { uuid }.join("/")
end

def govuk_subdomain_url
subdomain = @random.rand(2..4).times.map {
("a".."z").to_a.sample(@random.rand(3..8), random: @random).join
}.join(".")
"https://#{subdomain}.gov.uk#{base_path}"
end

def string(minimum_chars = nil, maximum_chars = nil)
minimum_chars ||= 0
maximum_chars ||= 100
WORDS.sample(@random.rand(minimum_chars..maximum_chars), random: @random).join(" ")
end

def bool
@random.rand(2) == 1
end

def anchor
"##{hex}"
end

def random_identifier(separator:)
WORDS.sample(@random.rand(1..10), random: @random)
.join("-")
.gsub(/[^a-z0-9\-_]+/i, "-")
.gsub("-", separator)
end

def uuid
# matches uuid regex e.g. e058aad7-ce86-5181-8801-4ddcb3c8f27c
# /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/
"#{hex(8)}-#{hex(4)}-1#{hex(3)}-a#{hex(3)}-#{hex(12)}"
end

def hex(length = 10)
length.times.map { bool ? random_letter : random_number }.join("")
end

def string_for_regex(pattern)
case pattern.to_s
when "^(placeholder|placeholder_.+)$"
["placeholder", "placeholder_#{WORDS.sample(random: @random)}"].sample(random: @random)
when "^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$"
uuid
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?$"
base_path
when "^[1-9][0-9]{3}[-/](0[1-9]|1[0-2])[-/](0[1-9]|[12][0-9]|3[0-1])$"
Date.today.iso8601
when "^[1-9][0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[0-1])$"
Date.today.iso8601
when "^#.+$"
anchor
when "[a-z-]"
random_identifier(separator: "-")
when "^[a-z_]+$"
random_identifier(separator: "_")
when "^/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?$"
base_path
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)+campaign\\.gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
govuk_subdomain_url
when "^https://([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[A-Za-z0-9])?\\.)*gov\\.uk(/(([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})+(/([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)*)?(\\?([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?(#([a-zA-Z0-9._~!$&'()*+,;=:@-]|%[0-9a-fA-F]{2})*)?)?$"
govuk_subdomain_url
when '[a-z0-9\-_]'
"#{hex}-#{hex}"
else
raise <<-DOC
Don't know how to generate random string for pattern #{pattern.inspect}
This propably means you've introduced a new regex in govuk-content-schemas.
Because it's very hard to generate a valid string from a regex alone,
we have to specify a method to generate random data for each regex in
the schemas.
To fix this:
- Add your regex to `lib/govuk_schemas/random.rb`
DOC
end
end

private

def random_letter
letters = ("a".."f").to_a
letters[@random.rand(0..letters.count - 1)]
end

def random_number
numbers = ("0".."9").to_a
numbers[@random.rand(0..numbers.count - 1)]
end
end
end
13 changes: 9 additions & 4 deletions lib/govuk_schemas/random_example.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
require "govuk_schemas/random"
require "govuk_schemas/random_item_generator"
require "govuk_schemas/random_schema_generator"
require "json-schema"
require "json"

Expand All @@ -24,11 +23,17 @@ class RandomExample
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
# GovukSchemas::RandomExample.new(schema: schema).payload
#
# Example with seed (for consistent results):
#
# schema = GovukSchemas::Schema.find(frontend_schema: "detailed_guide")
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload
# GovukSchemas::RandomExample.new(schema: schema, seed: 777).payload # returns same as above
#
# @param [Hash] schema A JSON schema.
# @return [GovukSchemas::RandomExample]
def initialize(schema:)
def initialize(schema:, seed: nil)
@schema = schema
@random_generator = RandomItemGenerator.new(schema: schema)
@random_generator = RandomSchemaGenerator.new(schema: schema, seed: seed)
end

# Returns a new `GovukSchemas::RandomExample` object.
Expand Down
Loading

0 comments on commit e05adef

Please sign in to comment.