Skip to content

Instantly share code, notes, and snippets.

@akapug
Created November 25, 2025 23:49
Show Gist options
  • Select an option

  • Save akapug/51ade9cac8b16d64fd094820dcae9b39 to your computer and use it in GitHub Desktop.

Select an option

Save akapug/51ade9cac8b16d64fd094820dcae9b39 to your computer and use it in GitHub Desktop.
pug@pugtop:~$ curl -sL https://github.com/elide-dev/elide/pull/1770.diff
diff --git a/packages/graalvm/src/main/kotlin/elide/runtime/gvm/internals/intrinsics/js/crypto/WebCryptoIntrinsic.kt b/packages/graalvm/src/main/kotlin/elide/runtime/gvm/internals/intrinsics/js/crypto/WebCryptoIntrinsic.kt
index 863e10b9c5..978d70c7a8 100644
--- a/packages/graalvm/src/main/kotlin/elide/runtime/gvm/internals/intrinsics/js/crypto/WebCryptoIntrinsic.kt
+++ b/packages/graalvm/src/main/kotlin/elide/runtime/gvm/internals/intrinsics/js/crypto/WebCryptoIntrinsic.kt
@@ -27,6 +27,7 @@ import elide.runtime.gvm.internals.intrinsics.js.typed.UUIDValue
import elide.runtime.intrinsics.GuestIntrinsic
import elide.runtime.intrinsics.js.Crypto.Companion.MAX_RANDOM_BYTES_SIZE
import elide.runtime.intrinsics.js.SubtleCrypto
+import elide.runtime.intrinsics.js.err.AbstractJsException
import elide.runtime.intrinsics.js.err.QuotaExceededError
import elide.runtime.intrinsics.js.err.ValueError
import elide.runtime.intrinsics.js.typed.UUID
diff --git a/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/CryptoAPI.kt b/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/CryptoAPI.kt
index 509221a3e6..fe48d702bb 100644
--- a/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/CryptoAPI.kt
+++ b/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/CryptoAPI.kt
@@ -14,6 +14,7 @@ package elide.runtime.intrinsics.js.node
import org.graalvm.polyglot.Value
import elide.annotations.API
+import elide.runtime.intrinsics.js.node.crypto.RandomIntCallback
import elide.vm.annotations.Polyglot
/**
@@ -30,4 +31,56 @@ import elide.vm.annotations.Polyglot
* @return A randomly generated 36 character UUID c4 string in lowercase format (e.g. "5cb34cef-5fc2-47e4-a3ac-4bb055fa2025")
*/
@Polyglot public fun randomUUID(options: Value? = null): String
+
+ /**
+ * ## Crypto: `randomInt`
+ * Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+ *
+ * See also: [Node Crypto API: `randomInt`](https://nodejs.org/api/crypto.html#cryptorandomintmin-max-callback)
+ *
+ * @param min Lower bound (inclusive)
+ * @param max Upper bound (exclusive)
+ * @param callback Callback to receive the generated safe integer or an error.
+ * @return Unit
+ */
+ @Polyglot public fun randomInt(min: Long, max: Long, callback: RandomIntCallback)
+
+ /**
+ * ## Crypto: randomInt
+ * Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+ *
+ * See also: [Node Crypto API: `randomInt`](https://nodejs.org/api/crypto.html#cryptorandomintmin-max-callback)
+ *
+ * @param min Lower bound (inclusive)
+ * @param max Upper bound (exclusive)
+ * @return A randomly generated safe integer between `min` (inclusive) and `max` (exclusive).
+ */
+ @Polyglot public fun randomInt(min: Long, max: Long): Long
+
+ /**
+ * ## Crypto: randomInt
+ * Public overload of [randomInt].
+ * Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+ * @param min Lower bound (inclusive)
+ * @param max Upper bound (exclusive)
+ * @param callback Callback to receive the generated safe integer or an error.
+ */
+ @Polyglot public fun randomInt(min: Value, max: Value, callback: Value)
+
+ /**
+ * ## Crypto: randomInt
+ * Public overload of [randomInt].
+ * Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+ * @param min Lower bound (inclusive)
+ * @param max Upper bound (exclusive)
+ */
+ @Polyglot public fun randomInt(min: Value, max: Value): Long
+
+ /**
+ * ## Crypto: randomInt
+ * Public overload of [randomInt].
+ * Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+ * @param max Upper bound (exclusive)
+ */
+ @Polyglot public fun randomInt(max: Value): Long
}
diff --git a/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/crypto/CryptoOptions.kt b/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/crypto/CryptoOptions.kt
new file mode 100644
index 0000000000..603ffdaee4
--- /dev/null
+++ b/packages/graalvm/src/main/kotlin/elide/runtime/intrinsics/js/node/crypto/CryptoOptions.kt
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2024-2025 Elide Technologies, Inc.
+ *
+ * Licensed under the MIT license (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * https://opensource.org/license/mit/
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under the License.
+ */
+package elide.runtime.intrinsics.js.node.crypto
+
+/**
+ * ## Callback: `crypto.randomInt`
+ *
+ * Describes the callback function shape which is provided to the `randomInt` operation.
+ */
+public typealias RandomIntCallback = (err: Throwable?, value: Long?) -> Unit
diff --git a/packages/graalvm/src/main/kotlin/elide/runtime/node/crypto/NodeCrypto.kt b/packages/graalvm/src/main/kotlin/elide/runtime/node/crypto/NodeCrypto.kt
index 3407a12395..4fbaaef773 100644
--- a/packages/graalvm/src/main/kotlin/elide/runtime/node/crypto/NodeCrypto.kt
+++ b/packages/graalvm/src/main/kotlin/elide/runtime/node/crypto/NodeCrypto.kt
@@ -14,8 +14,10 @@ package elide.runtime.node.crypto
import org.graalvm.polyglot.Value
import org.graalvm.polyglot.proxy.ProxyExecutable
+import java.math.BigInteger
import elide.runtime.gvm.api.Intrinsic
import elide.runtime.gvm.internals.intrinsics.js.AbstractNodeBuiltinModule
+import elide.runtime.gvm.js.JsError
import elide.runtime.gvm.js.JsSymbol.JsSymbols.asJsSymbol
import elide.runtime.gvm.loader.ModuleInfo
import elide.runtime.gvm.loader.ModuleRegistry
@@ -24,12 +26,85 @@ import elide.runtime.intrinsics.GuestIntrinsic.MutableIntrinsicBindings
import elide.runtime.intrinsics.js.node.CryptoAPI
import elide.runtime.lang.javascript.NodeModuleName
import elide.vm.annotations.Polyglot
+import java.security.SecureRandom
+import kotlin.concurrent.thread
+import elide.runtime.intrinsics.js.err.RangeError
+import elide.runtime.intrinsics.js.err.TypeError
+import elide.runtime.intrinsics.js.node.crypto.RandomIntCallback
// Internal symbol where the Node built-in module is installed.
private const val CRYPTO_MODULE_SYMBOL = "node_${NodeModuleName.CRYPTO}"
-// Functiopn name for randomUUID
+// Function name for randomUUID
private const val F_RANDOM_UUID = "randomUUID"
+private const val F_RANDOM_INT = "randomInt"
+
+// Cached Int generator to ensure we don't create multiple instances.
+private val cryptoRandomGenerator by lazy { SecureRandom() }
+
+// The maximum range (max - min) allowed is 2^48 in Node.js.
+private val MAX_48_BIT_LIMIT = BigInteger.valueOf(2L).pow(48)
+
+// Generates a cryptographically secure random integer between the specified `min` (inclusive) and `max` (exclusive) values.
+private fun genRandomInt(min: Long, max: Long): Long {
+ try {
+ return cryptoRandomGenerator.nextLong(min, max)
+ } catch (e: Throwable) {
+ throw TypeError.create("Error generating random bytes for randomInt: ${e.message}")
+ }
+}
+
+// Safely converts a Value to a BigInteger, ensuring it is a safe integer within JS limits.
+private fun safeValueToBigInt(value: Value, name: String): BigInteger {
+ if (value.isNumber) {
+ val bigIntValue: BigInteger? = when {
+ value.fitsInLong() -> {
+ BigInteger.valueOf(value.asLong())
+ }
+ // Reject integers that exceed Long.MAX_VALUE or are less than Long.MIN_VALUE
+ value.fitsInBigInteger() -> throw RangeError.create("The \"$name\" argument must be a safe integer. Received an integer that exceeds the max bounds ${MAX_48_BIT_LIMIT}.")
+ // Reject non-integer numbers
+ value.fitsInDouble() -> {
+ throw TypeError.create("The \"$name\" argument must be a safe integer. Received a non-integer number: ${value.asDouble()}.")
+ }
+ else -> null // Reject non-integer (e.g. Infinity, NaN, very large BigInts)
+ }
+
+ // Define JS safe integer bounds
+ val jsMaxSafeInt = BigInteger("9007199254740991") // 2^53 - 1
+ val jsMinSafeInt = BigInteger("-9007199254740991") // -(2^53 - 1)
+
+ // Final check: even if conversion works, ensure it falls within JS safe limits
+ if (bigIntValue != null && bigIntValue >= jsMinSafeInt && bigIntValue <= jsMaxSafeInt) {
+ return bigIntValue
+ }
+ }
+ // Invalid value type, we don't want it
+ throw TypeError.create("The \"$name\" argument must be a safe integer. Received ${value}.")
+}
+
+// Validates that the provided min and max values are safe integers and that the range difference does not exceed 2^48.
+private fun genSafeRange(min: Value, max: Value): Pair<Long, Long> {
+ // Safely convert both inputs to BigInteger
+ val minBigInt = safeValueToBigInt(min, "min")
+ val maxBigInt = safeValueToBigInt(max, "max")
+
+ // Enforce the Min <= Max rule otherwise we throw a RangeError
+ if (minBigInt >= maxBigInt) {
+ throw RangeError.create("The value of \"max\" is out of range. It must be greater than the value of \"min\" (${minBigInt}). Received ${maxBigInt}.")
+ }
+
+ val rangeDifference = maxBigInt.subtract(minBigInt)
+
+ // If the range difference exceeds 2^48, we throw a RangeError. Node.js has a range limit of 2^48 for randomInt.
+ if (rangeDifference > MAX_48_BIT_LIMIT) {
+ println("Range difference exceeds 2^48 limit: $rangeDifference")
+ throw RangeError.create("The value of \"max - min\" is out of range. It must be <= 281474976710655. Received ${rangeDifference}.")
+ }
+
+ // Return the validated safe Long values
+ return Pair(minBigInt.toLong(), maxBigInt.toLong())
+}
// Installs the Node crypto module into the intrinsic bindings.
@Intrinsic internal class NodeCryptoModule : AbstractNodeBuiltinModule() {
@@ -46,14 +121,13 @@ private const val F_RANDOM_UUID = "randomUUID"
* # Node API: `crypto`
*/
internal class NodeCrypto private constructor () : ReadOnlyProxyObject, CryptoAPI {
- //
-
internal companion object {
@JvmStatic fun create(): NodeCrypto = NodeCrypto()
// Module members
private val moduleMembers = arrayOf(
F_RANDOM_UUID,
+ F_RANDOM_INT,
).apply { sort() }
}
@@ -64,10 +138,51 @@ internal class NodeCrypto private constructor () : ReadOnlyProxyObject, CryptoAP
return java.util.UUID.randomUUID().toString()
}
+ @Polyglot override fun randomInt(min: Long, max: Long): Long {
+ return genRandomInt(min, max)
+ }
+
+ @Polyglot override fun randomInt(min: Long, max: Long, callback: RandomIntCallback) {
+ val randomValue = genRandomInt(min, max)
+
+ thread {
+ try {
+ callback.invoke(null, randomValue)
+ } catch (e: Throwable) {
+ callback.invoke(TypeError.create(e.message ?: "Unknown error"), randomValue)
+ }
+ }
+ }
+
+ @Polyglot override fun randomInt(min: Value, max: Value, callback: Value) {
+ val (safeMin, safeMax) = genSafeRange(min, max)
+
+ val safeCallback: RandomIntCallback = callback.let { cb ->
+ { err: Throwable?, value: Long? ->
+ cb.execute(
+ err?.let { Value.asValue(it) },
+ value?.let { Value.asValue(it) }
+ )
+ }
+ }
+
+ return randomInt(safeMin, safeMax, safeCallback)
+ }
+
+ @Polyglot override fun randomInt(min: Value, max: Value): Long {
+ val (safeMin, safeMax) = genSafeRange(min, max)
+ return randomInt(safeMin, safeMax)
+ }
+
+ @Polyglot override fun randomInt(max: Value): Long {
+ val (safeMin, safeMax) = genSafeRange(Value.asValue(0), max)
+ return randomInt(safeMin, safeMax)
+ }
+
// ProxyObject implementation
override fun getMemberKeys(): Array<String> = moduleMembers
- override fun hasMember(key: String): Boolean =
+ override fun hasMember(key: String): Boolean =
moduleMembers.binarySearch(key) >= 0
override fun getMember(key: String): Any? = when (key) {
@@ -76,6 +191,31 @@ internal class NodeCrypto private constructor () : ReadOnlyProxyObject, CryptoAP
val options = args.getOrNull(0)
randomUUID(options)
}
+ F_RANDOM_INT -> ProxyExecutable { args ->
+ // Check if last argument is a callback function
+ val lastIsCb = args.lastOrNull()?.canExecute() == true
+
+ when (args.size) {
+ 1 -> {
+ // randomInt(max)
+ this.randomInt(args[0])
+ }
+ 2 -> {
+ if (lastIsCb) {
+ // randomInt(max, callback)
+ this.randomInt(Value.asValue(0), args[0], args.last())
+ } else {
+ // randomInt(min, max)
+ this.randomInt(args[0], args[1])
+ }
+ }
+ 3 -> {
+ // randomInt(min, max, callback)
+ this.randomInt(args[0], args[1], args.last())
+ }
+ else -> throw JsError.typeError("Invalid number of arguments for crypto.randomInt: ${args.size}")
+ }
+ }
else -> null
}
}
diff --git a/packages/graalvm/src/test/kotlin/elide/runtime/node/NodeCryptoTest.kt b/packages/graalvm/src/test/kotlin/elide/runtime/node/NodeCryptoTest.kt
index eb564fbdd9..e9476af3e7 100644
--- a/packages/graalvm/src/test/kotlin/elide/runtime/node/NodeCryptoTest.kt
+++ b/packages/graalvm/src/test/kotlin/elide/runtime/node/NodeCryptoTest.kt
@@ -12,13 +12,21 @@
*/
package elide.runtime.node
+import org.graalvm.polyglot.Value
+import org.junit.jupiter.api.assertThrows
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import kotlin.test.Test
import kotlin.test.assertEquals
+import kotlin.test.assertFailsWith
import kotlin.test.assertIs
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
import kotlin.test.assertNotNull
+import kotlin.test.assertNull
import elide.annotations.Inject
+import elide.runtime.intrinsics.js.err.RangeError
+import elide.runtime.intrinsics.js.err.TypeError
import elide.runtime.node.crypto.NodeCryptoModule
import elide.testing.annotations.TestCase
@@ -198,4 +206,316 @@ import elide.testing.annotations.TestCase
assert.equal(typeof uuid2, "string");
"""
}
+
+ @Test fun `randomInt should return a Long when valid min and max are provided with no callback`() = conforms {
+ val min = Value.asValue(5L)
+ val max = Value.asValue(10L)
+ val result = crypto.provide().randomInt(min, max)
+
+ assertIs<Long>(result)
+ assertTrue(result in 5 until 10)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const result = crypto.randomInt(5, 10);
+
+ assert.equal(typeof result, "number");
+ assert.ok(result >= 5 && result < 10);
+ """
+ }
+
+ @Test fun `randomInt should throw a RangeError when min is greater than or equal to max`() = conforms {
+
+ assertFailsWith<RangeError> { crypto.provide().randomInt(Value.asValue(10L), Value.asValue(10L)) }
+ assertFailsWith<RangeError> { crypto.provide().randomInt(Value.asValue(10L), Value.asValue( 5L)) }
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ assert.throws(() => crypto.randomInt(10, 10), RangeError);
+ assert.throws(() => crypto.randomInt(10, 5), RangeError);
+ """
+ }
+
+ @Test fun `randomInt should default min to 0 when only max is provided`() = conforms {
+ val result = crypto.provide().randomInt(Value.asValue(5L))
+
+ assertIs<Long>(result)
+ assertTrue(result in 0 until 5)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const result = crypto.randomInt(5);
+
+ assert.equal(typeof result, "number");
+ assert.ok(result >= 0 && result < 5);
+ """
+ }
+
+ @Test fun `randomInt should invoke callback when callback is provided`() = conforms {
+ val latch = CountDownLatch(1)
+ var called = false
+ val result = crypto.provide().randomInt(10L, 20L) { err, value ->
+ called = true
+ assertNull(err)
+ assertTrue(value in 10L until 20L)
+ latch.countDown()
+ }
+ assertIs<Unit>(result)
+ latch.await(1, TimeUnit.SECONDS)
+ assertTrue(called)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto")
+ const assert = require("assert")
+
+ function randomIntPromise(min, max) {
+ return new Promise((resolve, reject) => {
+ crypto.randomInt(min, max, (err, int) => {
+ callbackInvoked = true;
+ assert.equal(err, null, "Callback error should be null");
+ resolve(int);
+ });
+ });
+ };
+
+ let callbackInvoked = false;
+
+ randomIntPromise(10, 20)
+ .then((int) => {
+ assert.equal(typeof int, "number");
+ assert.ok(int >= 10 && int < 20, "randomInt should be within the range");
+ assert.ok(callbackInvoked, "Callback should have been invoked");
+ })
+ """
+ }
+
+ @Test fun `randomInt should return min when range is 1`() = conforms {
+ val min = 7L
+ val max = 8L
+
+ val result = crypto.provide().randomInt(min,max)
+
+ assertEquals(min, result)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = 7;
+ const max = 8;
+
+ const result = crypto.randomInt(min, max);
+
+ assert.equal(result, min);
+ """
+ }
+
+ @Test fun `randomInt should handle large ranges correctly`() = conforms {
+ val min = 0L
+ val max = 100_000_000_000L
+ val result = crypto.provide().randomInt(min, max)
+
+ assertIs<Long>(result)
+ assertTrue(result in min until max)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = 0;
+ const max = 100000000000;
+ const result = crypto.randomInt(min, max);
+
+ assert.ok(result >= min && result < max);
+ """
+ }
+
+ @Test fun `randomInt should throw TypeError for non-numeric arguments`() = conforms {
+
+ val invalidMin: Value = Value.asValue("a")
+ val validMax: Value = Value.asValue(10L)
+ val validMin: Value = Value.asValue(0L)
+ val invalidMax: Value = Value.asValue("b")
+
+ assertFailsWith<TypeError> { crypto.provide().randomInt(invalidMin, validMax) }
+ assertFailsWith<TypeError> { crypto.provide().randomInt(validMin, invalidMax) }
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ assert.throws(() => crypto.randomInt("a", 10), TypeError);
+ assert.throws(() => crypto.randomInt(0, "b"), TypeError);
+ """
+ }
+
+ @Test fun `randomInt callback should be async`() = conforms {
+ var callbackCalled = false
+ val min = 0L
+ val max = 10L
+
+ fun callbackFn (err: Throwable?, value: Long?) {
+ callbackCalled = true
+
+ if (err != null) {
+ throw err
+ } else {
+ assertTrue(value in min until max)
+ }
+ }
+
+ val result = crypto.provide().randomInt(min, max, ::callbackFn)
+ // Callback should not have been called yet
+ assertTrue(!callbackCalled, "Callback should not have been invoked yet")
+
+ // The result should be Unit since a callback was provided
+ assertIs<Unit>(result)
+
+ // Callback should have been called by this point
+ assertTrue(callbackCalled, "Callback should have been invoked asynchronously")
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ let callbackCalled = false;
+ crypto.randomInt(1, 10, (err, val) => {
+ callbackCalled = true;
+
+ assert.equal(err, null);
+ assert.ok(val >= 1 && val < 10);
+ assert.ok(callbackCalled, "Callback should have been invoked asynchronously");
+ });
+
+ assert.equal(callbackCalled, false);
+ """
+ }
+
+ @Test fun `randomInt should throw TypeError for float arguments`() = conforms {
+ assertFailsWith<TypeError> { crypto.provide().randomInt(Value.asValue(1.5), Value.asValue(10L)) }
+ assertFailsWith<TypeError> { crypto.provide().randomInt(Value.asValue(1L), Value.asValue(10.5)) }
+ assertFailsWith<TypeError> { crypto.provide().randomInt(Value.asValue(1.5), Value.asValue(10.5)) }
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ assert.throws(() => crypto.randomInt(1.5, 10), TypeError);
+ assert.throws(() => crypto.randomInt(1, 10.5), TypeError);
+ assert.throws(() => crypto.randomInt(1.5, 10.5), TypeError);
+ """
+ }
+
+ @Test fun `randomInt should throw when range exceeds MAX_SAFE_INTEGER`() = conforms {
+ val min = Value.asValue(-9007199254740991L)
+ val max = Value.asValue(9007199254740991L)
+
+ assertFailsWith<RangeError> { crypto.provide().randomInt(min, max) }
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = Number.MIN_SAFE_INTEGER;
+ const max = Number.MAX_SAFE_INTEGER;
+
+ assert.throws(() => crypto.randomInt(min, max), RangeError);
+ """
+ }
+
+ @Test fun `randomInt large positive range`() = conforms {
+ val min = Value.asValue(0L)
+ val max = Value.asValue(281474976710655L)
+
+ val result = crypto.provide().randomInt(min, max)
+
+ assertTrue(result in 0L until 281474976710655L)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = 0;
+ const max = 281474976710655;
+ const result = crypto.randomInt(min, max);
+
+ assert.ok(result >= min && result < max, "Value is within the expected range");
+ """
+ }
+
+ @Test fun `randomInt large negative range`() = conforms {
+ val min = -9_000_000_000_000L
+ val max = 0L
+ val result = crypto.provide().randomInt(min, max)
+
+ // @TODO The assertion is causing failures, possibly due to number type coercion?
+ assertTrue(result in -9000000000000L until 0L)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = -9000000000000;
+ const max = 0;
+ const result = crypto.randomInt(min, max);
+
+ assert.ok(result >= min && result < max, "Value is within the expected range");
+ """
+ }
+
+ @Test fun `randomInt max safe integer`() = conforms {
+ val min = Value.asValue(0)
+ val max = Value.asValue(9007199254740991)
+
+ assertThrows<RangeError>{ crypto.provide().randomInt(min, max) }
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = 0;
+ const max = Number.MAX_SAFE_INTEGER;
+
+ assert.throws(() => crypto.randomInt(min, max), RangeError);
+ """
+ }
+
+ @Test fun `randomInt range crossing zero`() = conforms {
+ val min = -100L
+ val max = 100L
+ val result = crypto.provide().randomInt(min, max)
+
+ assertTrue(result in min until max)
+ }.guest {
+ //language=javascript
+ """
+ const crypto = require("crypto");
+ const assert = require("assert");
+
+ const min = -100;
+ const max = 100;
+ const result = crypto.randomInt(min, max);
+
+ assert.ok(result >= min && result < max);
+ """
+ }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment