From 373d67e070f2ac1a1e3d8f3adbe3822fc7fab70e Mon Sep 17 00:00:00 2001 From: Shinigami92 Date: Thu, 7 Apr 2022 16:08:24 +0200 Subject: [PATCH 1/3] feat: resetable unique store --- src/unique.ts | 2 ++ src/utils/unique.ts | 47 +++++++++++++++++++++++++++++---------------- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/unique.ts b/src/unique.ts index ff8698e695d..93efe10b0dd 100644 --- a/src/unique.ts +++ b/src/unique.ts @@ -104,6 +104,7 @@ export class Unique { * @param options.currentIterations This parameter does nothing. * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. + * @param options.store The store of unique entries. Defaults to a global store. * * @example * faker.unique(faker.name.firstName) // 'Corbin' @@ -118,6 +119,7 @@ export class Unique { currentIterations?: number; exclude?: RecordKey | RecordKey[]; compare?: (obj: Record, key: RecordKey) => 0 | -1; + store?: Record; } = {} ): ReturnType { const { maxTime = this._maxTime, maxRetries = this._maxRetries } = options; diff --git a/src/utils/unique.ts b/src/utils/unique.ts index 4e7fadcecbb..df1b827ecf8 100644 --- a/src/utils/unique.ts +++ b/src/utils/unique.ts @@ -4,7 +4,7 @@ export type RecordKey = string | number | symbol; /** * Global store of unique values. - * This means that faker should *never* return duplicate values across all API methods when using `Faker.unique`. + * This means that faker should *never* return duplicate values across all API methods when using `Faker.unique` without passing `options.store`. */ const GLOBAL_UNIQUE_STORE: Record = {}; @@ -14,11 +14,6 @@ const GLOBAL_UNIQUE_STORE: Record = {}; */ const GLOBAL_UNIQUE_EXCLUDE: RecordKey[] = []; -/** - * Current iteration or retries of `unique.exec` (current loop depth). - */ -const currentIterations = 0; - /** * Uniqueness compare function. * Default behavior is to check value as key against object hash. @@ -43,15 +38,21 @@ function defaultCompare( * @param startTime The time the execution started. * @param now The current time. * @param code The error code. + * @param store The store of unique entries. + * @param currentIterations Current iteration or retries of `unique.exec` (current loop depth). * * @throws The given error code with additional text. */ -function errorMessage(startTime: number, now: number, code: string): never { +function errorMessage( + startTime: number, + now: number, + code: string, + store: Record, + currentIterations: number +): never { console.error('Error', code); console.log( - `Found ${ - Object.keys(GLOBAL_UNIQUE_STORE).length - } unique entries before throwing error. + `Found ${Object.keys(store).length} unique entries before throwing error. retried: ${currentIterations} total time: ${now - startTime}ms` ); @@ -77,6 +78,7 @@ Try adjusting maxTime or maxRetries parameters for faker.unique().` * @param options.currentIterations The current attempt. Defaults to `0`. * @param options.exclude The value or values that should be excluded/skipped. Defaults to `[]`. * @param options.compare The function used to determine whether a value was already returned. Defaults to check the existence of the key. + * @param options.store The store of unique entries. Defaults to `GLOBAL_UNIQUE_STORE`. */ export function exec RecordKey>( method: Method, @@ -88,6 +90,7 @@ export function exec RecordKey>( currentIterations?: number; exclude?: RecordKey | RecordKey[]; compare?: (obj: Record, key: RecordKey) => 0 | -1; + store?: Record; } = {} ): ReturnType { const now = new Date().getTime(); @@ -97,6 +100,7 @@ export function exec RecordKey>( maxTime = 50, maxRetries = 50, compare = defaultCompare, + store = GLOBAL_UNIQUE_STORE, } = options; let { exclude = GLOBAL_UNIQUE_EXCLUDE } = options; options.currentIterations = options.currentIterations ?? 0; @@ -112,22 +116,31 @@ export function exec RecordKey>( // console.log(now - startTime) if (now - startTime >= maxTime) { - return errorMessage(startTime, now, `Exceeded maxTime: ${maxTime}`); + return errorMessage( + startTime, + now, + `Exceeded maxTime: ${maxTime}`, + store, + options.currentIterations + ); } if (options.currentIterations >= maxRetries) { - return errorMessage(startTime, now, `Exceeded maxRetries: ${maxRetries}`); + return errorMessage( + startTime, + now, + `Exceeded maxRetries: ${maxRetries}`, + store, + options.currentIterations + ); } // Execute the provided method to find a potential satisfied value. const result: ReturnType = method.apply(this, args); // If the result has not been previously found, add it to the found array and return the value as it's unique. - if ( - compare(GLOBAL_UNIQUE_STORE, result) === -1 && - exclude.indexOf(result) === -1 - ) { - GLOBAL_UNIQUE_STORE[result] = result; + if (compare(store, result) === -1 && exclude.indexOf(result) === -1) { + store[result] = result; options.currentIterations = 0; return result; } else { From 65c881367eba4b4c073bf4628b0709f77419a388 Mon Sep 17 00:00:00 2001 From: Shinigami92 Date: Thu, 7 Apr 2022 16:21:32 +0200 Subject: [PATCH 2/3] test: add test for user-specific store --- test/unique.spec.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/unique.spec.ts b/test/unique.spec.ts index 6f26a0c5067..926226c1b6e 100644 --- a/test/unique.spec.ts +++ b/test/unique.spec.ts @@ -210,4 +210,22 @@ Try adjusting maxTime or maxRetries parameters for faker.unique().`) expect(options.exclude).toBe(exclude); expect(options.compare).toBe(compare); }); + + it('should be possible to pass a user-specific store', () => { + const store = {}; + + const method = () => 'with conflict: 0'; + + expect(store).toEqual({}); + + expect(faker.unique(method, [], { store })).toBe('with conflict: 0'); + expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' }); + + expect(() => faker.unique(method, [], { store })).toThrow(); + + delete store['with conflict: 0']; + + expect(faker.unique(method, [], { store })).toBe('with conflict: 0'); + expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' }); + }); }); From 59edce34cb5a8b51d46fa692989957c859412c12 Mon Sep 17 00:00:00 2001 From: Shinigami Date: Thu, 7 Apr 2022 16:59:35 +0200 Subject: [PATCH 3/3] test: remove initial store check --- test/unique.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/unique.spec.ts b/test/unique.spec.ts index 926226c1b6e..071231663b8 100644 --- a/test/unique.spec.ts +++ b/test/unique.spec.ts @@ -216,8 +216,6 @@ Try adjusting maxTime or maxRetries parameters for faker.unique().`) const method = () => 'with conflict: 0'; - expect(store).toEqual({}); - expect(faker.unique(method, [], { store })).toBe('with conflict: 0'); expect(store).toEqual({ 'with conflict: 0': 'with conflict: 0' });