Repository: serpro69/kotlin-faker (kotlin)

Issue:
Add sha-generators
Generating fake sha hashes can be quite useful and should be added to faker. Probably either to `RandomService` or via new provider like `Crypto`?

Pull request description:

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 4b8592d0cf..24ec0dd04f 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -6,6 +6,7 @@
 
 * https://github.com/serpro69/kotlin-faker/pull/134[#134] Overload `RandomService#randomSublist` and `RandomService#randomSubset` with `sizeRange` parameter
 * https://github.com/serpro69/kotlin-faker/pull/144[#144] Add index and punctuation chars support to `RandomService#randomString`
+* https://github.com/serpro69/kotlin-faker/pull/154[#154] New Cryptography provider for generating random sha sums
 
 [discrete]
 === Changed
@@ -14,11 +15,13 @@
 
 [discrete]
 === Fixed
-* https://github.com/serpro69/kotlin-faker/issues/140[#140] Fix NPE when generating CurrencySymbol with non 'en' locale
-* https://github.com/serpro69/kotlin-faker/pull/142[#142] Fix unique localized category keys missing from dictionary
 * https://github.com/serpro69/kotlin-faker/issues/136[#136] Parameter 'streets' not found in 'ADDRESS' category
 * https://github.com/serpro69/kotlin-faker/issues/137[#137] Parameter 'category' not found in 'COMPANY' category for 'ja' locale
+* https://github.com/serpro69/kotlin-faker/issues/138[#138] Parameter 'zip_code' not found in 'ADDRESS' category for 'fr' locale
+* https://github.com/serpro69/kotlin-faker/issues/140[#140] Fix NPE when generating CurrencySymbol with non 'en' locale
+* https://github.com/serpro69/kotlin-faker/pull/142[#142] Fix unique localized category keys missing from dictionary
 * https://github.com/serpro69/kotlin-faker/pull/146[#146] Fix `RandomService#randomString` for some eng-lang locales
+* https://github.com/serpro69/kotlin-faker/issues/148[#148] Fix StarWars quotes by character
 
 [discrete]
 === Docs
diff --git a/cli-bot/src/main/resources/META-INF/native-image/io.github.serpro69/cli-bot/reflect-config.json b/cli-bot/src/main/resources/META-INF/native-image/io.github.serpro69/cli-bot/reflect-config.json
index b0ee5a75df..23686111a2 100644
--- a/cli-bot/src/main/resources/META-INF/native-image/io.github.serpro69/cli-bot/reflect-config.json
+++ b/cli-bot/src/main/resources/META-INF/native-image/io.github.serpro69/cli-bot/reflect-config.json
@@ -196,6 +196,10 @@
         "name": "getCrossfit",
         "parameterTypes": []
       },
+      {
+        "name": "getCrypto",
+        "parameterTypes": []
+      },
       {
         "name": "getCryptoCoin",
         "parameterTypes": []
@@ -4973,6 +4977,35 @@
       }
     ]
   },
+  {
+    "name": "io.github.serpro69.kfaker.provider.misc.CryptographyProvider",
+    "methods": [
+      {
+        "name": "md5",
+        "parameterTypes": []
+      },
+      {
+        "name": "sha1",
+        "parameterTypes": []
+      },
+      {
+        "name": "sha224",
+        "parameterTypes": []
+      },
+      {
+        "name": "sha256",
+        "parameterTypes": []
+      },
+      {
+        "name": "sha384",
+        "parameterTypes": []
+      },
+      {
+        "name": "sha512",
+        "parameterTypes": []
+      }
+    ]
+  },
   {
     "name": "io.github.serpro69.kfaker.provider.misc.StringProvider"
   },
diff --git a/core/src/main/kotlin/io/github/serpro69/kfaker/Faker.kt b/core/src/main/kotlin/io/github/serpro69/kfaker/Faker.kt
index 0e1f08181d..f25c758d02 100644
--- a/core/src/main/kotlin/io/github/serpro69/kfaker/Faker.kt
+++ b/core/src/main/kotlin/io/github/serpro69/kfaker/Faker.kt
@@ -3,6 +3,7 @@
 package io.github.serpro69.kfaker
 
 import io.github.serpro69.kfaker.provider.*
+import io.github.serpro69.kfaker.provider.misc.CryptographyProvider
 import io.github.serpro69.kfaker.provider.misc.StringProvider
 import io.github.serpro69.kfaker.provider.unique.GlobalUniqueDataDataProvider
 
@@ -23,6 +24,7 @@ class Faker @JvmOverloads constructor(internal val config: FakerConfig = fakerCo
 
     val unique by lazy { GlobalUniqueDataDataProvider() }
 
+    val crypto: CryptographyProvider by lazy { CryptographyProvider(fakerService) }
     val randomProvider: RandomProvider by lazy { RandomProvider(config) }
     val string: StringProvider by lazy { StringProvider(fakerService) }
 
@@ -67,6 +69,7 @@ class Faker @JvmOverloads constructor(internal val config: FakerConfig = fakerCo
     val commerce: Commerce by lazy { Commerce(fakerService) }
     val community: Community by lazy { Community(fakerService) }
     val company: Company by lazy { Company(fakerService) }
+
     //    val compass: Compass by lazy {Compass(fakerService) }
     val computer: Computer by lazy { Computer(fakerService) }
     val conan: Conan by lazy { Conan(fakerService) }
@@ -132,6 +135,7 @@ class Faker @JvmOverloads constructor(internal val config: FakerConfig = fakerCo
     val idNumber: IdNumber by lazy { IdNumber(fakerService) }
     val industrySegments: IndustrySegments by lazy { IndustrySegments(fakerService) }
     val internet: Internet by lazy { Internet(fakerService) }
+
     //    val invoice: Invoice by lazy {Invoice(fakerService }
     val jackHandey: JackHandey by lazy { JackHandey(fakerService) }
     val job: Job by lazy { Job(fakerService) }
@@ -187,6 +191,7 @@ class Faker @JvmOverloads constructor(internal val config: FakerConfig = fakerCo
     val simpsons: Simpsons by lazy { Simpsons(fakerService) }
     val slackEmoji: SlackEmoji by lazy { SlackEmoji(fakerService) }
     val sonicTheHedgehog: SonicTheHedgehog by lazy { SonicTheHedgehog(fakerService) }
+
     //    val source: Source by lazy {Source(fakerService }
     val southPark: SouthPark by lazy { SouthPark(fakerService) }
     val space: Space by lazy { Space(fakerService) }
@@ -233,7 +238,7 @@ class Faker @JvmOverloads constructor(internal val config: FakerConfig = fakerCo
     /**
      * DSL builder for creating instances of [Faker]
      */
-    class Builder internal constructor(){
+    class Builder internal constructor() {
         /**
          * @property config faker configuration for the [Faker] instance
          * which will be created with this [Faker.Builder].
diff --git a/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/CryptographyProvider.kt b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/CryptographyProvider.kt
new file mode 100644
index 0000000000..f9393e8765
--- /dev/null
+++ b/core/src/main/kotlin/io/github/serpro69/kfaker/provider/misc/CryptographyProvider.kt
@@ -0,0 +1,50 @@
+package io.github.serpro69.kfaker.provider.misc
+
+import io.github.serpro69.kfaker.FakerService
+import io.github.serpro69.kfaker.dictionary.Category
+import io.github.serpro69.kfaker.faker
+import io.github.serpro69.kfaker.provider.AbstractFakeDataProvider
+import io.github.serpro69.kfaker.provider.unique.LocalUniqueDataProvider
+import io.github.serpro69.kfaker.provider.unique.UniqueProviderDelegate
+
+class CryptographyProvider internal constructor(
+    fakerService: FakerService
+) : AbstractFakeDataProvider<CryptographyProvider>(fakerService) {
+    override val category: Category = Category.ofName("CRYPTOGRAPHY")
+    override val localUniqueDataProvider = LocalUniqueDataProvider<CryptographyProvider>()
+    override val unique: CryptographyProvider by UniqueProviderDelegate(localUniqueDataProvider)
+
+    /**
+     * Generates and returns a pseudo-randomly generated md5 hash value, 32 digits long.
+     */
+    fun md5(): String = sha(32)
+
+    /**
+     * Generates and returns a pseudo-randomly generated sha-1 hash value, 40 digits long.
+     */
+    fun sha1(): String = sha(40)
+
+    /**
+     * Generates and returns a pseudo-randomly generated sha-224 hash value, 56 digits long.
+     */
+    fun sha224(): String = sha(56)
+
+    /**
+     * Generates and returns a pseudo-randomly generated sha-256 hash value, 64 digits long.
+     */
+    fun sha256(): String = sha(64)
+
+    /**
+     * Generates and returns a pseudo-randomly generated sha-384 hash value, 96 digits long.
+     */
+    fun sha384(): String = sha(96)
+
+    /**
+     * Generates and returns a pseudo-randomly generated sha-512 hash value, 128 digits long.
+     */
+    fun sha512(): String = sha(128)
+
+    private fun sha(len: Int): String = with(fakerService) {
+        resolveUniqueValue({ """[a-f0-9]{$len}""".generexify() }, "generexify")
+    }
+}
diff --git a/docs/src/orchid/resources/pages/data-provider/crypto.adoc b/docs/src/orchid/resources/pages/data-provider/crypto.adoc
new file mode 100644
index 0000000000..2518cad3bb
--- /dev/null
+++ b/docs/src/orchid/resources/pages/data-provider/crypto.adoc
@@ -0,0 +1,23 @@
+---
+---
+
+== `Faker().crypto`
+
+.Available Functions
+[%collapsible]
+====
+[source,kotlin]
+----
+Faker().crypto.md5() // => fc798bf6aaba1fe4e7ecbf6fe27af92e
+
+Faker().crypto.sha1() // => 4b136b53f9bd55bca6cdf1a238bb528ef86ebc3b
+
+Faker().crypto.sha224() // => 2f58ad3a57958ffabd9ecd0ff9aacddb8f1c04cd8db8df6ce2dcc328
+
+Faker().crypto.sha256() // => 31fc0cbfdbfa09d7069dc2ce1cde0d439f9eb420ae42265cdcc5de852cbf1adf
+
+Faker().crypto.sha384() // => 0a1d8cc70ba3ea166a348c34b6e695fefdceb6663fd8842b96336cbaf5e5dcbe7415cdbc4b8f210aece3fc9ed8abce0f
+
+Faker().crypto.sha512() // => 2c1ddfb86b3bb10d1f7bae26fcff20e756ede4708c6e55fea8d9cd8d0855ace61ff27265a31cc0645ac0f662d6d6cd3aad33efbed0c3f6f966d920f3bc6de005
+----
+====


Test patch:
diff --git a/cli-bot/src/test/kotlin/io/github/serpro69/kfaker/app/cli/IntrospectorTest.kt b/cli-bot/src/test/kotlin/io/github/serpro69/kfaker/app/cli/IntrospectorTest.kt
index 4474dbcfb0..10f3635107 100644
--- a/cli-bot/src/test/kotlin/io/github/serpro69/kfaker/app/cli/IntrospectorTest.kt
+++ b/cli-bot/src/test/kotlin/io/github/serpro69/kfaker/app/cli/IntrospectorTest.kt
@@ -1,6 +1,7 @@
 package io.github.serpro69.kfaker.app.cli
 
 import io.github.serpro69.kfaker.Faker
+import io.github.serpro69.kfaker.faker
 import io.github.serpro69.kfaker.provider.Address
 import io.github.serpro69.kfaker.provider.Dota
 import io.kotest.core.spec.style.DescribeSpec
@@ -65,6 +66,7 @@ class IntrospectorTest : DescribeSpec() {
                         "Control",
                         "Cosmere",
                         "Crossfit",
+                        "CryptographyProvider",
                         "CryptoCoin",
                         "CultureSeries",
                         "Currency",
@@ -273,6 +275,7 @@ class IntrospectorTest : DescribeSpec() {
                         "control",
                         "cosmere",
                         "crossfit",
+                        "crypto",
                         "cryptoCoin",
                         "cultureSeries",
                         "currency",
@@ -480,3 +483,14 @@ class IntrospectorTest : DescribeSpec() {
         }
     }
 }
+
+fun main() {
+    val f = faker { }
+
+    println(f.crypto.md5())
+    println(f.crypto.sha1())
+    println(f.crypto.sha224())
+    println(f.crypto.sha256())
+    println(f.crypto.sha384())
+    println(f.crypto.sha512())
+}
diff --git a/core/src/test/kotlin/io/github/serpro69/kfaker/RandomServiceTest.kt b/core/src/test/kotlin/io/github/serpro69/kfaker/RandomServiceTest.kt
index fb0330fec2..8cc5f13af9 100644
--- a/core/src/test/kotlin/io/github/serpro69/kfaker/RandomServiceTest.kt
+++ b/core/src/test/kotlin/io/github/serpro69/kfaker/RandomServiceTest.kt
@@ -262,7 +262,7 @@ internal class RandomServiceTest : DescribeSpec({
                 val sublist = randomService.randomSublist(list, size = 10, shuffled = true)
                 assertSoftly {
                     list shouldContainAll sublist
-                    if (sublist.isNotEmpty()) sublist shouldNotBeSortedWith Comparator { o1, o2 -> o1.compareTo(o2) }
+                    if (sublist.size > 1) sublist shouldNotBeSortedWith Comparator { o1, o2 -> o1.compareTo(o2) }
                 }
             }
         }
@@ -298,10 +298,10 @@ internal class RandomServiceTest : DescribeSpec({
             }
 
             it("should return a random shuffled subset") {
-                val sublist = randomService.randomSubset(set, shuffled = true)
+                val subset = randomService.randomSubset(set, shuffled = true)
                 assertSoftly {
-                    set shouldContainAll sublist
-                    if (sublist.isNotEmpty()) sublist shouldNotBeSortedWith Comparator { o1, o2 -> o1.compareTo(o2) }
+                    set shouldContainAll subset
+                    if (subset.size > 1) subset shouldNotBeSortedWith Comparator { o1, o2 -> o1.compareTo(o2) }
                 }
             }
         }
diff --git a/core/src/test/kotlin/io/github/serpro69/kfaker/provider/CryptographyProviderTest.kt b/core/src/test/kotlin/io/github/serpro69/kfaker/provider/CryptographyProviderTest.kt
new file mode 100644
index 0000000000..36e93f14bb
--- /dev/null
+++ b/core/src/test/kotlin/io/github/serpro69/kfaker/provider/CryptographyProviderTest.kt
@@ -0,0 +1,34 @@
+package io.github.serpro69.kfaker.provider
+
+import io.github.serpro69.kfaker.Faker
+import io.kotest.core.spec.style.DescribeSpec
+import io.kotest.matchers.string.shouldMatch
+
+class CryptographyProviderTest : DescribeSpec({
+    describe("Cryptography provider") {
+        val crypto = Faker().crypto
+
+        it("should generate a valid md5 value") {
+            crypto.md5() isValidWithLen 32
+        }
+        it("should generate a valid sha-1 value") {
+            crypto.sha1() isValidWithLen 40
+        }
+        it("should generate a valid sha-224 value") {
+            crypto.sha224() isValidWithLen 56
+        }
+        it("should generate a valid sha-256 value") {
+            crypto.sha256() isValidWithLen 64
+        }
+        it("should generate a valid sha-384 value") {
+            crypto.sha384() isValidWithLen 96
+        }
+        it("should generate a valid sha-512 value") {
+            crypto.sha512() isValidWithLen 128
+        }
+    }
+})
+
+private infix fun String.isValidWithLen(len: Int) {
+    this shouldMatch Regex("""^[a-f0-9]{$len}$""")
+}