Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/random.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";
import { createRandomStringGenerator } from "./random";
import { getRandomValues } from "uncrypto";

describe("createRandomStringGenerator", () => {
it("generates a random string of specified length", () => {
Expand Down Expand Up @@ -55,4 +56,40 @@ describe("createRandomStringGenerator", () => {
true,
);
});

it("combines multiple alphabets when passed during generation", () => {
// Mock getRandomValues to return sequentially increasing values
vi.mock("uncrypto", () => ({
getRandomValues: vi.fn(
<T extends ArrayBufferView | null>(array: T): T => {
if (array instanceof Uint8Array) {
for (let i = 0; i < array.length; i++) {
array[i] = i % 256; // Predictable sequence for testing
}
}
return array;
},
),
}));

try {
const generator = createRandomStringGenerator("a-z");
// Generate a long string to ensure all characters are represented
const randomString = generator(256, "A-Z", "0-9");

// The combined alphabet we expect
const expectedAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

// Should use all characters from the expected alphabet
expect(
[...expectedAlphabet].every((char) => randomString.includes(char)),
).toBe(true);

// Additionally verify that the string has expected length
expect(randomString).toHaveLength(256);
} finally {
// Restore the original implementation
vi.restoreAllMocks();
}
});
});
23 changes: 10 additions & 13 deletions src/random.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,28 @@ function expandAlphabet(alphabet: Alphabet): string {
}

export function createRandomStringGenerator<A extends Alphabet>(
...characters: A[]
...baseAlphabets: A[]
) {
const baseCharacterSet = characters.map(expandAlphabet).join("");
if (baseCharacterSet.length === 0) {
const baseCharSet = baseAlphabets.map(expandAlphabet).join("");
if (baseCharSet.length === 0) {
throw new Error(
"No valid characters provided for random string generation.",
);
}

const baseCharSetLength = baseCharacterSet.length;
const baseCharSetLength = baseCharSet.length;

return <SubA extends Alphabet>(
length: number,
...[alphabet]: [SubA?, ...SubA[]]
) => {
return <SubA extends Alphabet>(length: number, ...alphabets: SubA[]) => {
if (length <= 0) {
throw new Error("Length must be a positive integer.");
}

let characterSet = baseCharacterSet;
let charSet = baseCharSet;
let charSetLength = baseCharSetLength;

if (alphabet) {
characterSet = expandAlphabet(alphabet);
charSetLength = characterSet.length;
if (alphabets.length > 0) {
charSet = alphabets.map(expandAlphabet).join("");
charSetLength = charSet.length;
}

const charArray = new Uint8Array(length);
Expand All @@ -51,7 +48,7 @@ export function createRandomStringGenerator<A extends Alphabet>(
let result = "";
for (let i = 0; i < length; i++) {
const index = charArray[i] % charSetLength;
result += characterSet[index];
result += charSet[index];
}

return result;
Expand Down
Loading