Skip to content

Commit 802a0de

Browse files
committed
Extract chunk-random-string in random.clj
...from near identical alphabetic-string and random-ascii-string. Also adds a bit of test coverage for these functions and some of their adjacents. Signed-off-by: Josh Partlow <jpartlow@glatisant.org>
1 parent 173de8b commit 802a0de

2 files changed

Lines changed: 48 additions & 32 deletions

File tree

src/puppetlabs/puppetdb/random.clj

Lines changed: 24 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,51 +10,43 @@
1010
(def ^{:doc "Convenience for java.util.Random"}
1111
random (java.util.Random.))
1212

13-
(defn- alphabetic-string
14-
"Generate a random alphabetic string of the given length.
15-
16-
Chunks requests to stay within the BouncyCastle FIPS DRBG per-request limit
17-
of 262144 bits. Apache Commons Lang3's CachedRandomBits allocates
18-
(count * gapBits + 3) / 5 + 10 bytes per call. For nextAlphabetic
19-
(A-Z + a-z = 52 chars, gapBits = ceil(log2(52)) = 6) the maximum count per call is:
20-
(count * 6 + 3) / 5 + 10 <= 32768 => count <= 27298"
21-
[length]
22-
(let [fips-max-chunk 27298
23-
^RandomStringUtils secure-random (RandomStringUtils/secure)]
24-
(if (<= length fips-max-chunk)
25-
(.nextAlphabetic secure-random length)
26-
;; Build incrementally to avoid intermediate vectors/strings for large values.
27-
(let [^StringBuilder builder (StringBuilder. length)]
28-
(loop [remaining length]
29-
(if (<= remaining 0)
30-
(.toString builder)
31-
(let [chunk-size (min remaining fips-max-chunk)]
32-
(.append builder (.nextAlphabetic secure-random chunk-size))
33-
(recur (- remaining chunk-size)))))))))
34-
35-
(defn random-ascii-string
36-
"Generate a random ASCII string of the given length.
13+
(defn- chunked-random-string
14+
"Generate a random alphabetic or ascii string of the given length.
3715
3816
Chunks requests to stay within the BouncyCastle FIPS DRBG per-request limit
3917
of 262144 bits (32768 bytes). Apache Commons Lang3's RandomStringUtils
4018
allocates a CachedRandomBits with size = (count * gapBits + 3) / 5 + 10
41-
bytes. For nextAscii (ASCII range 32-127, gap=95, gapBits=7) the maximum
42-
count that keeps the cache within 32768 bytes is:
43-
(count * 7 + 3) / 5 + 10 <= 32768 => count <= 23398"
44-
[length]
45-
(let [fips-max-chunk 23398
46-
^RandomStringUtils secure-random (RandomStringUtils/secure)]
19+
bytes.
20+
21+
For nextAscii (ASCII range 32-127, gap=95, gapBits=7)
22+
the maximum count that keeps the cache within 32768 bytes is:
23+
(count * 7 + 3) / 5 + 10 <= 32768 => count <= 23398
24+
25+
For nextAlphabetic (A-Z + a-z = 52 chars, gapBits = ceil(log2(52)) = 6)
26+
the maximum count per call is:
27+
(count * 6 + 3) / 5 + 10 <= 32768 => count <= 27298"
28+
[method length]
29+
(let [^RandomStringUtils secure-random (RandomStringUtils/secure)
30+
[gen-fn fips-max-chunk] (case method
31+
:ascii [#(.nextAscii ^RandomStringUtils secure-random %) 23398]
32+
:alphabetic [#(.nextAlphabetic ^RandomStringUtils secure-random %) 27298])]
4733
(if (<= length fips-max-chunk)
48-
(.nextAscii secure-random length)
34+
(gen-fn length)
4935
;; Build incrementally to avoid intermediate vectors/strings for large values.
5036
(let [^StringBuilder builder (StringBuilder. length)]
5137
(loop [remaining length]
5238
(if (<= remaining 0)
5339
(.toString builder)
5440
(let [chunk-size (min remaining fips-max-chunk)]
55-
(.append builder (.nextAscii secure-random chunk-size))
41+
(.append builder (gen-fn chunk-size))
5642
(recur (- remaining chunk-size)))))))))
5743

44+
(defn- alphabetic-string [length]
45+
(chunked-random-string :alphabetic length))
46+
47+
(defn random-ascii-string [length]
48+
(chunked-random-string :ascii length))
49+
5850
(defn random-string
5951
"Generate a random string of optional length"
6052
([] (alphabetic-string (inc (rand-int 10))))

test/puppetlabs/puppetdb/random_test.clj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[clojure.test :refer :all]
44
[puppetlabs.puppetdb.random
55
:refer [distribute
6+
random-ascii-string
67
random-bool
78
random-node-name
89
random-pp-path
@@ -16,24 +17,47 @@
1617

1718
(deftest test-random-string
1819
(testing "should return a string of specified length"
20+
(is (>= 10 (count (random-string))))
1921
(is (= 8 (count (random-string 8))))
2022
(is (= 30 (count (random-string 30))))
2123
(is (= 100 (count (random-string 100)))))
2224

25+
(testing "should only contain alphabetic characters"
26+
(is (re-matches #"\A[a-zA-Z]+\z" (random-string)))
27+
(is (re-matches #"\A[a-zA-Z]{100}\z" (random-string 100))))
28+
2329
(testing "should only accept a positive integer"
2430
(is (thrown? IllegalArgumentException (random-string -1)))
2531
(is (thrown? ClassCastException (random-string "asdf")))))
2632

2733
(deftest test-random-string-alpha
2834
(testing "should return a string of specified length"
35+
(is (>= 10 (count (random-string-alpha))))
2936
(is (= 8 (count (random-string-alpha 8))))
3037
(is (= 30 (count (random-string-alpha 30))))
3138
(is (= 100 (count (random-string-alpha 100)))))
3239

40+
(testing "should only contain lowercase alphabetic characters"
41+
(is (re-matches #"\A[a-z]+\z" (random-string-alpha)))
42+
(is (re-matches #"\A[a-z]{100}\z" (random-string-alpha 100))))
43+
3344
(testing "should only accept a positive integer"
3445
(is (thrown? IllegalArgumentException (random-string-alpha -1)))
3546
(is (thrown? ClassCastException (random-string-alpha "asdf")))))
3647

48+
(deftest test-random-ascii-string
49+
(testing "should return a string of specified length"
50+
(is (= 8 (count (random-ascii-string 8))))
51+
(is (= 30 (count (random-ascii-string 30))))
52+
(is (= 100 (count (random-ascii-string 100)))))
53+
54+
(testing "should only contain ascii characters"
55+
(is (re-matches #"\A[\x20-\x7E]{100}\z" (random-ascii-string 100))))
56+
57+
(testing "should only accept a positive integer"
58+
(is (thrown? IllegalArgumentException (random-ascii-string -1)))
59+
(is (thrown? ClassCastException (random-ascii-string "asdf")))))
60+
3761
(deftest test-random-bool
3862
(testing "should return a boolean"
3963
(is (boolean? (random-bool)))))

0 commit comments

Comments
 (0)