Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: reimplement datatype.bigInt #791

Merged
merged 8 commits into from
May 23, 2022
59 changes: 49 additions & 10 deletions src/modules/datatype/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,20 +261,59 @@ export class Datatype {
/**
* Returns a [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#bigint_type) number.
*
* @param value When provided, this method simply converts it to `BigInt` type.
* @param options Maximum value or options object.
* @param options.min Lower bound for generated bigint. Defaults to `0n`.
* @param options.max Upper bound for generated bigint. Defaults to `min + 999999999999999n`.
*
* @throws When options define `max < min`.
*
* @example
* faker.datatype.bigInt() // 8507209999914928n
* faker.datatype.bigInt('156') // 156n
* faker.datatype.bigInt(30) // 30n
* faker.datatype.bigInt(3000n) // 3000n
* faker.datatype.bigInt(true) // 1n
* faker.datatype.bigInt() // 55422n
* faker.datatype.bigInt(100n) // 52n
* faker.datatype.bigInt({ min: 1000000n }) // 431433n
* faker.datatype.bigInt({ max: 100n }) // 42n
* faker.datatype.bigInt({ min: 10n, max: 100n }) // 36n
*/
bigInt(value?: string | number | bigint | boolean): bigint {
if (value === undefined) {
value = Math.floor(this.number() * 99999999999) + 10000000000;
bigInt(
options?:
| bigint
| boolean
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would faker.datatype.bigInt(false) return? I don't understand what advantage we have from accepting a boolean parameter.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

It's just the official constructor argument

| number
| string
| {
min?: bigint | boolean | number | string;
max?: bigint | boolean | number | string;
}
): bigint {
let min: bigint;
let max: bigint;

if (typeof options === 'object') {
min = BigInt(options.min ?? 0);
max = BigInt(options.max ?? min + BigInt(999999999999999));
} else {
min = BigInt(0);
max = BigInt(options ?? 999999999999999);
Copy link
Member

@xDivisionByZerox xDivisionByZerox May 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can you pass options directly here? MDN documentation states that the value can "May be a string or an integer". So why the boolean or the object?

I've never worked with BigInt so sorry if this makes no sense.

}

return BigInt(value);
if (max === min) {
return min;
}

if (max < min) {
throw new FakerError(`Max ${max} should be larger then min ${min}.`);
}

const delta = max - min;

const offset =
BigInt(
this.faker.random.numeric(delta.toString(10).length, {
allowLeadingZeros: true,
})
) %
(delta + BigInt(1));

return min + offset;
}
}
97 changes: 90 additions & 7 deletions test/datatype.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { faker } from '../src';
import { faker, FakerError } from '../src';

const seededRuns = [
{
Expand Down Expand Up @@ -69,8 +69,8 @@ const seededRuns = [
length: [79654, '2eiXX/J/*&', 86617, 60111],
},
bigInt: {
noArgs: 3745409999962546n,
value: 42n,
noArgs: 379177551410048n,
value: 37n,
},
},
},
Expand Down Expand Up @@ -141,8 +141,8 @@ const seededRuns = [
length: [56052, 21258, 54308, 3397],
},
bigInt: {
noArgs: 2620209999973798n,
value: 42n,
noArgs: 251225403255239n,
value: 25n,
},
},
},
Expand Down Expand Up @@ -213,8 +213,8 @@ const seededRuns = [
length: ['Kti5-}$_/`', 76408, 35403, 69406],
},
bigInt: {
noArgs: 9285209999907148n,
value: 42n,
noArgs: 948721906162743n,
value: 8n,
},
},
},
Expand Down Expand Up @@ -417,6 +417,19 @@ describe('datatype', () => {
const actual = faker.datatype.bigInt(42);
expect(actual).toEqual(expectations.bigInt.value);
});

it('should throw when min > max', () => {
const min = 10000n;
const max = 999n;

faker.seed(seed);

expect(() => {
faker.datatype.bigInt({ min, max });
}).toThrowError(
new FakerError(`Max ${max} should be larger then min ${min}.`)
);
});
});
});
}
Expand Down Expand Up @@ -701,6 +714,76 @@ describe('datatype', () => {
const generateBigInt = faker.datatype.bigInt();
expect(generateBigInt).toBeTypeOf('bigint');
});

it('should generate a big bigInt value with low delta', () => {
const min = 999999999n;
const max = 1000000000n;
const generateBigInt = faker.datatype.bigInt({ min, max });
expect(generateBigInt).toBeTypeOf('bigint');
expect(generateBigInt).toBeGreaterThanOrEqual(min);
expect(generateBigInt).toBeLessThanOrEqual(max);
});

it('should return a random bigint given a maximum value as BigInt', () => {
const max = 10n;
expect(faker.datatype.bigInt(max)).toBeGreaterThanOrEqual(0n);
expect(faker.datatype.bigInt(max)).toBeLessThanOrEqual(max);
});

it('should return a random bigint given a maximum value as Object', () => {
const options = { max: 10n };
expect(faker.datatype.bigInt(options)).toBeGreaterThanOrEqual(0n);
expect(faker.datatype.bigInt(options)).toBeLessThanOrEqual(
options.max
);
});

it('should return a random bigint given a maximum value of 0', () => {
const options = { max: 0n };
expect(faker.datatype.bigInt(options)).toBe(0n);
});

it('should return a random bigint given a negative bigint minimum and maximum value of 0', () => {
const options = { min: -100n, max: 0n };
expect(faker.datatype.bigInt(options)).toBeGreaterThanOrEqual(
options.min
);
expect(faker.datatype.bigInt(options)).toBeLessThanOrEqual(
options.max
);
});

it('should return a random bigint between a range', () => {
const options = { min: 22, max: 33 };
for (let i = 0; i < 100; i++) {
const randomBigInt = faker.datatype.bigInt(options);
expect(randomBigInt).toBeGreaterThanOrEqual(options.min);
expect(randomBigInt).toBeLessThanOrEqual(options.max);
}
});

it('should succeed with success-rate', () => {
const min = 0n;
const max = 1000000000000n;
const randomBigInt = faker.datatype.bigInt({ min, max });
expect(randomBigInt).toBeGreaterThanOrEqual(min);
expect(randomBigInt).toBeLessThanOrEqual(max);
});

it('should not mutate the input object', () => {
const initialMin = 1n;
const initialOtherProperty = 'hello darkness my old friend';
const input: {
min?: bigint;
max?: bigint;
otherProperty: string;
} = Object.freeze({
min: initialMin,
otherProperty: initialOtherProperty,
});

expect(() => faker.datatype.bigInt(input)).not.toThrow();
});
});
}
});
Expand Down