Skip to content

Commit

Permalink
refactor: datatype number (#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
Shinigami92 authored Apr 8, 2022
1 parent 0dfe9a3 commit 651d684
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 43 deletions.
25 changes: 13 additions & 12 deletions src/datatype.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { Faker } from '.';
import { FakerError } from './errors/faker-error';
import { deprecated } from './internal/deprecated';

/**
Expand All @@ -24,7 +25,7 @@ export class Datatype {
* @param options.max Upper bound for generated number. Defaults to `min + 99999`.
* @param options.precision Precision of the generated number. Defaults to `1`.
*
* @throws When options define `max < min`
* @throws When options define `max < min`.
*
* @example
* faker.datatype.number() // 55422
Expand All @@ -35,25 +36,25 @@ export class Datatype {
* faker.datatype.number({ min: 10, max: 100, precision: 0.01 }) // 36.94
*/
number(
options?: number | { min?: number; max?: number; precision?: number }
options: number | { min?: number; max?: number; precision?: number } = 99999
): number {
const opts = typeof options === 'number' ? { max: options } : options ?? {};
if (typeof options === 'number') {
options = { max: options };
}

const min = typeof opts.min === 'number' ? opts.min : 0;
let max = typeof opts.max === 'number' ? opts.max : min + 99999;
const precision = typeof opts.precision === 'number' ? opts.precision : 1;
const { min = 0, precision = 1 } = options;
const max = options.max ?? min + 99999;

if (max < min) {
throw new Error(`Max ${max} should be larger then min ${min}`);
if (max === min) {
return min;
}

// Make the range inclusive of the max value
if (max >= 0) {
max += precision;
if (max < min) {
throw new FakerError(`Max ${max} should be larger then min ${min}.`);
}

const randomNumber = Math.floor(
this.faker.mersenne.rand(max / precision, min / precision)
this.faker.mersenne.rand(max / precision + 1, min / precision)
);

// Workaround problem in float point arithmetics for e.g. 6681493 / 0.01
Expand Down
92 changes: 61 additions & 31 deletions test/datatype.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ const seededRuns = [
number: new Date('2000-06-14T02:54:42.082Z'),
withMin: new Date('2065-11-10T19:27:20.915Z'),
withMax: new Date('2001-03-20T11:14:25.251Z'),
withMinMax: new Date('1789-03-26T15:44:45.218Z'),
withMinMax: new Date('1789-03-26T15:44:45.219Z'),
},
string: {
noArgs: 'wKti5-}$_/',
Expand Down Expand Up @@ -297,7 +297,7 @@ describe('datatype', () => {

expect(() => {
faker.datatype.number({ min, max });
}).toThrowError(`Max ${max} should be larger then min ${min}`);
}).toThrowError(`Max ${max} should be larger then min ${min}.`);
});
});

Expand Down Expand Up @@ -431,38 +431,73 @@ describe('datatype', () => {
describe('number', () => {
it('should return a random number given a maximum value as Number', () => {
const max = 10;
expect(faker.datatype.number(max)).greaterThanOrEqual(0);
expect(faker.datatype.number(max)).lessThanOrEqual(max);

const actual = faker.datatype.number(max);

expect(actual).greaterThanOrEqual(0);
expect(actual).lessThanOrEqual(max);
});

it('should return a random number given a maximum value as Object', () => {
const options = { max: 10 };
expect(faker.datatype.number(options)).greaterThanOrEqual(0);
expect(faker.datatype.number(options)).lessThanOrEqual(options.max);

const actual = faker.datatype.number(options);

expect(actual).greaterThanOrEqual(0);
expect(actual).lessThanOrEqual(options.max);
});

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

const actual = faker.datatype.number(options);

expect(actual).toBe(0);
});

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

const actual = faker.datatype.number(options);

expect(actual).greaterThanOrEqual(options.min);
expect(actual).lessThanOrEqual(options.max);
});

it('should return a random number between a range', () => {
const options = { min: 22, max: 33 };
for (let i = 0; i < 100; i++) {
const randomNumber = faker.datatype.number(options);
expect(randomNumber).greaterThanOrEqual(options.min);
expect(randomNumber).lessThanOrEqual(options.max);
const actual = faker.datatype.number(options);
expect(actual).greaterThanOrEqual(options.min);
expect(actual).lessThanOrEqual(options.max);
}
});

it('should return inclusive negative max value', () => {
let foundNegative4 = false;
let foundNegative5 = false;

for (let iter = 0; iter < 1000; iter++) {
const actual = faker.datatype.number({ min: -5, max: -4 });

if (actual === -4) {
foundNegative4 = true;
} else if (actual === -5) {
foundNegative5 = true;
}

expect(actual).greaterThanOrEqual(-5);
expect(actual).lessThanOrEqual(-4);

if (foundNegative4 && foundNegative5) {
break;
}
}

expect(foundNegative4).toBeTruthy();
expect(foundNegative5).toBeTruthy();
});

it('provides numbers with a given precision', () => {
const options = { min: 0, max: 1.5, precision: 0.5 };
const results = Array.from(
Expand All @@ -481,32 +516,27 @@ describe('datatype', () => {
it('provides numbers with a with exact precision', () => {
const options = { min: 0.5, max: 0.99, precision: 0.01 };
for (let i = 0; i < 100; i++) {
const number = faker.datatype.number(options);
expect(number).toBe(Number(number.toFixed(2)));
const actual = faker.datatype.number(options);
expect(actual).toBe(Number(actual.toFixed(2)));
}
});

it('should not mutate the input object', () => {
const initalMin = 1;
const initalPrecision = 1;
const initalOtherProperty = 'hello darkness my old friend';
const initialMin = 1;
const initialPrecision = 1;
const initialOtherProperty = 'hello darkness my old friend';
const input: {
min?: number;
max?: number;
precision?: number;
otherProperty: string;
} = {
min: initalMin,
precision: initalPrecision,
otherProperty: initalOtherProperty,
};

faker.datatype.number(input);

expect(input.min).toBe(initalMin);
expect(input.precision).toBe(initalPrecision);
expect(input.max).toBe(undefined);
expect(input.otherProperty).toBe(initalOtherProperty);
} = Object.freeze({
min: initialMin,
precision: initialPrecision,
otherProperty: initialOtherProperty,
});

expect(() => faker.datatype.number(input)).not.toThrow();
});
});

Expand Down

0 comments on commit 651d684

Please sign in to comment.