From 719cc67769776ac3211442f65c0ec8323d2fccbc Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:09:15 +0100 Subject: [PATCH 1/2] Reduce likelyhood of random number conflicts By using the non-secure random generator, and caching the class that may be forked in Puma, we're making conflicts more likely. This is still a possibility of collisions - we've almost used 1% of available codes, however this might reduce the change. I've removed the random number seed as part of this - SecureRandom can't be seeded in the same way. Looking at the commit history, this was a tool to help make test runs more deterministic but isn't required. If we do, we should add the ability to swap out random number generators. --- lib/for_education_code_generator.rb | 8 +++----- spec/lib/for_education_code_generator_spec.rb | 4 ++-- spec/support/for_education_code_generator.rb | 8 -------- 3 files changed, 5 insertions(+), 15 deletions(-) delete mode 100644 spec/support/for_education_code_generator.rb diff --git a/lib/for_education_code_generator.rb b/lib/for_education_code_generator.rb index f0da81148..88257258d 100644 --- a/lib/for_education_code_generator.rb +++ b/lib/for_education_code_generator.rb @@ -1,14 +1,12 @@ # frozen_string_literal: true +require 'securerandom' + class ForEducationCodeGenerator MAX_CODE = 1_000_000 - cattr_accessor :random - - self.random ||= Random.new - def self.generate - number = random.rand(MAX_CODE) + number = SecureRandom.random_number(MAX_CODE) code = format('%06d', number) code.match(/(\d\d)(\d\d)(\d\d)/) do |m| diff --git a/spec/lib/for_education_code_generator_spec.rb b/spec/lib/for_education_code_generator_spec.rb index 2da0f6e1e..d8956b66e 100644 --- a/spec/lib/for_education_code_generator_spec.rb +++ b/spec/lib/for_education_code_generator_spec.rb @@ -4,8 +4,8 @@ RSpec.describe ForEducationCodeGenerator do describe '.generate' do - it 'uses Random#rand to generate a random number up to the maximum' do - allow(described_class.random).to receive(:rand).with(ForEducationCodeGenerator::MAX_CODE).and_return(123) + it 'uses SecureRandom to generate a random number up to the maximum' do + allow(SecureRandom).to receive(:random_number).with(ForEducationCodeGenerator::MAX_CODE).and_return(123) expect(described_class.generate).to eq('00-01-23') end diff --git a/spec/support/for_education_code_generator.rb b/spec/support/for_education_code_generator.rb deleted file mode 100644 index 53c74df24..000000000 --- a/spec/support/for_education_code_generator.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -RSpec.configure do |config| - config.before(:suite) do - Rails.logger.debug { "ForEducationCodeGenerator randomized with seed #{config.seed}" } - ForEducationCodeGenerator.random = Random.new(config.seed) - end -end From ebc1fd6689a4e40ab740001209204f47e08adab3 Mon Sep 17 00:00:00 2001 From: Chris Zetter <253059100+zetter-rpf@users.noreply.github.com> Date: Tue, 8 Sep 2026 16:19:25 +0100 Subject: [PATCH 2/2] remove spec that has 1 in 1000000 chance of failing --- spec/lib/for_education_code_generator_spec.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/lib/for_education_code_generator_spec.rb b/spec/lib/for_education_code_generator_spec.rb index d8956b66e..030cb231f 100644 --- a/spec/lib/for_education_code_generator_spec.rb +++ b/spec/lib/for_education_code_generator_spec.rb @@ -13,9 +13,5 @@ it 'generates a string containing 3 pairs of digits' do expect(described_class.generate).to match(/\d\d-\d\d-\d\d/) end - - it 'generates a different code each time' do - expect(described_class.generate).not_to eq(described_class.generate) - end end end