mirror of
https://github.com/CommunitySolidServer/CommunitySolidServer.git
synced 2024-10-03 14:55:10 +00:00
feat: Support async default values in getDefault
This commit is contained in:
parent
c73ef50e48
commit
a1e916b73a
@ -15,6 +15,7 @@ import { InternalServerError } from '../util/errors/InternalServerError';
|
||||
import { NotFoundHttpError } from '../util/errors/NotFoundHttpError';
|
||||
import type { IdentifierStrategy } from '../util/identifiers/IdentifierStrategy';
|
||||
import { IdentifierMap } from '../util/map/IdentifierMap';
|
||||
import { getDefault } from '../util/map/MapUtil';
|
||||
import { readableToQuads } from '../util/StreamUtil';
|
||||
import { ACL } from '../util/Vocabularies';
|
||||
import { getAccessControlledResources } from './AcpUtil';
|
||||
@ -78,12 +79,8 @@ export class AcpReader extends PermissionReader {
|
||||
// Extract all the policies relevant for the target
|
||||
const identifiers = this.getAncestorIdentifiers(target);
|
||||
for (const identifier of identifiers) {
|
||||
let acrs = resourceCache.get(identifier);
|
||||
if (!acrs) {
|
||||
const data = await this.readAcrData(identifier);
|
||||
acrs = [ ...getAccessControlledResources(data) ];
|
||||
resourceCache.set(identifier, acrs);
|
||||
}
|
||||
const acrs = await getDefault(resourceCache, identifier, async(): Promise<IAccessControlledResource[]> =>
|
||||
[ ...getAccessControlledResources(await this.readAcrData(identifier)) ]);
|
||||
const size = policies.length;
|
||||
policies.push(...this.getEffectivePolicies(target, acrs));
|
||||
this.logger.debug(`Found ${policies.length - size} policies relevant for ${target.path} in ${identifier.path}`);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { getLoggerFor } from '../logging/LogUtil';
|
||||
import { concat } from '../util/IterableUtil';
|
||||
import { IdentifierMap, IdentifierSetMultiMap } from '../util/map/IdentifierMap';
|
||||
import { getDefault } from '../util/map/MapUtil';
|
||||
import { ensureTrailingSlash, trimTrailingSlashes } from '../util/PathUtil';
|
||||
import type { PermissionReaderInput } from './PermissionReader';
|
||||
import { PermissionReader } from './PermissionReader';
|
||||
@ -44,11 +45,7 @@ export class PathBasedReader extends PermissionReader {
|
||||
for (const [ identifier, modes ] of accessMap) {
|
||||
const reader = this.findReader(identifier.path);
|
||||
if (reader) {
|
||||
let matches = result.get(reader);
|
||||
if (!matches) {
|
||||
matches = new IdentifierSetMultiMap();
|
||||
result.set(reader, matches);
|
||||
}
|
||||
const matches = getDefault(result, reader, (): AccessMap => new IdentifierSetMultiMap());
|
||||
matches.set(identifier, modes);
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ export class UnionPermissionReader extends UnionHandler<PermissionReader> {
|
||||
private mergePermissionMaps(permissionMap: PermissionMap, result: PermissionMap): void {
|
||||
for (const [ identifier, permissionSet ] of permissionMap) {
|
||||
for (const [ credential, permission ] of Object.entries(permissionSet) as [keyof PermissionSet, Permission][]) {
|
||||
const resultSet = getDefault(result, identifier, {});
|
||||
const resultSet = getDefault(result, identifier, (): PermissionSet => ({}));
|
||||
resultSet[credential] = this.mergePermissions(permission, resultSet[credential]);
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import type { ValuePreferences } from '../../http/representation/RepresentationPreferences';
|
||||
import { NotImplementedHttpError } from '../../util/errors/NotImplementedHttpError';
|
||||
import type { PromiseOrValue } from '../../util/PromiseUtil';
|
||||
import { getConversionTarget, getTypeWeight, preferencesToString } from './ConversionUtil';
|
||||
import type { RepresentationConverterArgs } from './RepresentationConverter';
|
||||
import { TypedRepresentationConverter } from './TypedRepresentationConverter';
|
||||
|
||||
type PromiseOrValue<T> = T | Promise<T>;
|
||||
type ValuePreferencesArg =
|
||||
PromiseOrValue<string> |
|
||||
PromiseOrValue<string[]> |
|
||||
|
@ -1,5 +1,29 @@
|
||||
import { types } from 'util';
|
||||
import { createAggregateError } from './errors/HttpErrorUtil';
|
||||
|
||||
export type PromiseOrValue<T> = T | Promise<T>;
|
||||
|
||||
/**
|
||||
* Verifies if the given value is a Promise or not.
|
||||
* @param object - Object to check.
|
||||
*/
|
||||
export function isPromise<T>(object: PromiseOrValue<T>): object is Promise<T> {
|
||||
return types.isPromise(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls `callback` with the resolved value of `object`.
|
||||
* In case `object` is a Promise, the result will also be a Promise,
|
||||
* otherwise the result will be sync.
|
||||
*/
|
||||
export function resolvePromiseOrValue<TIn, TOut>(object: PromiseOrValue<TIn>, callback: (val: TIn) => TOut):
|
||||
PromiseOrValue<TOut> {
|
||||
if (isPromise(object)) {
|
||||
return object.then((val): TOut => callback(val));
|
||||
}
|
||||
return callback(object);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
function noop(): void {}
|
||||
|
||||
|
@ -10,6 +10,7 @@ import { isHttpRequest } from '../server/HttpRequest';
|
||||
import { InternalServerError } from './errors/InternalServerError';
|
||||
import type { Guarded } from './GuardedStream';
|
||||
import { guardStream } from './GuardedStream';
|
||||
import type { PromiseOrValue } from './PromiseUtil';
|
||||
|
||||
export const endOfStream = promisify(eos);
|
||||
|
||||
@ -119,12 +120,12 @@ export interface AsyncTransformOptions<T = any> extends DuplexOptions {
|
||||
/**
|
||||
* Transforms data from the source by calling the `push` method
|
||||
*/
|
||||
transform?: (this: Transform, data: T, encoding: string) => any | Promise<any>;
|
||||
transform?: (this: Transform, data: T, encoding: string) => PromiseOrValue<any>;
|
||||
|
||||
/**
|
||||
* Performs any final actions after the source has ended
|
||||
*/
|
||||
flush?: (this: Transform) => any | Promise<any>;
|
||||
flush?: (this: Transform) => PromiseOrValue<any>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ReadWriteLocker } from './ReadWriteLocker';
|
||||
import type { ResourceLocker } from './ResourceLocker';
|
||||
|
||||
@ -12,11 +13,11 @@ export class EqualReadWriteLocker implements ReadWriteLocker {
|
||||
this.locker = locker;
|
||||
}
|
||||
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
return this.withLock(identifier, whileLocked);
|
||||
}
|
||||
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
return this.withLock(identifier, whileLocked);
|
||||
}
|
||||
|
||||
@ -26,7 +27,7 @@ export class EqualReadWriteLocker implements ReadWriteLocker {
|
||||
* @param identifier - Identifier of resource that needs to be locked.
|
||||
* @param whileLocked - Function to resolve while the resource is locked.
|
||||
*/
|
||||
private async withLock<T>(identifier: ResourceIdentifier, whileLocked: () => T | Promise<T>): Promise<T> {
|
||||
private async withLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
await this.locker.acquire(identifier);
|
||||
try {
|
||||
return await whileLocked();
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ReadWriteLocker } from './ReadWriteLocker';
|
||||
|
||||
/**
|
||||
@ -14,7 +15,7 @@ export interface ExpiringReadWriteLocker extends ReadWriteLocker {
|
||||
* @param whileLocked - A function to execute while the resource is locked.
|
||||
* Receives a callback as input parameter to maintain the lock.
|
||||
*/
|
||||
withReadLock: <T>(identifier: ResourceIdentifier, whileLocked: (maintainLock: () => void) => T | Promise<T>)
|
||||
withReadLock: <T>(identifier: ResourceIdentifier, whileLocked: (maintainLock: () => void) => PromiseOrValue<T>)
|
||||
=> Promise<T>;
|
||||
|
||||
/**
|
||||
@ -26,6 +27,6 @@ export interface ExpiringReadWriteLocker extends ReadWriteLocker {
|
||||
* @param whileLocked - A function to execute while the resource is locked.
|
||||
* Receives a callback as input parameter to maintain the lock.
|
||||
*/
|
||||
withWriteLock: <T>(identifier: ResourceIdentifier, whileLocked: (maintainLock: () => void) => T | Promise<T>)
|
||||
withWriteLock: <T>(identifier: ResourceIdentifier, whileLocked: (maintainLock: () => void) => PromiseOrValue<T>)
|
||||
=> Promise<T>;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import type { ResourceIdentifier } from '../../http/representation/ResourceIdent
|
||||
import type { KeyValueStorage } from '../../storage/keyvalue/KeyValueStorage';
|
||||
import { ForbiddenHttpError } from '../errors/ForbiddenHttpError';
|
||||
import { InternalServerError } from '../errors/InternalServerError';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ReadWriteLocker } from './ReadWriteLocker';
|
||||
import type { ResourceLocker } from './ResourceLocker';
|
||||
|
||||
@ -43,7 +44,7 @@ export class GreedyReadWriteLocker implements ReadWriteLocker {
|
||||
this.suffixes = suffixes;
|
||||
}
|
||||
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
await this.preReadSetup(identifier);
|
||||
try {
|
||||
return await whileLocked();
|
||||
@ -52,7 +53,7 @@ export class GreedyReadWriteLocker implements ReadWriteLocker {
|
||||
}
|
||||
}
|
||||
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
if (identifier.path.endsWith(`.${this.suffixes.count}`)) {
|
||||
throw new ForbiddenHttpError('This resource is used for internal purposes.');
|
||||
}
|
||||
@ -117,7 +118,7 @@ export class GreedyReadWriteLocker implements ReadWriteLocker {
|
||||
/**
|
||||
* Safely runs an action on the count.
|
||||
*/
|
||||
private async withInternalReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)):
|
||||
private async withInternalReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>):
|
||||
Promise<T> {
|
||||
const read = this.getReadLockKey(identifier);
|
||||
await this.locker.acquire(read);
|
||||
|
@ -1,4 +1,5 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
|
||||
/**
|
||||
* Allows the locking of resources which is needed for non-atomic {@link ResourceStore}s.
|
||||
@ -14,7 +15,7 @@ export interface ReadWriteLocker {
|
||||
*
|
||||
* @returns A promise resolving when the lock is released.
|
||||
*/
|
||||
withReadLock: <T>(identifier: ResourceIdentifier, whileLocked: () => T | Promise<T>) => Promise<T>;
|
||||
withReadLock: <T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>) => Promise<T>;
|
||||
|
||||
/**
|
||||
* Run the given function while the resource is locked.
|
||||
@ -26,5 +27,5 @@ export interface ReadWriteLocker {
|
||||
*
|
||||
* @returns A promise resolving when the lock is released.
|
||||
*/
|
||||
withWriteLock: <T>(identifier: ResourceIdentifier, whileLocked: () => T | Promise<T>) => Promise<T>;
|
||||
withWriteLock: <T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>) => Promise<T>;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import type { Initializable } from '../../init/Initializable';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import type { AttemptSettings } from '../LockUtils';
|
||||
import { retryFunction } from '../LockUtils';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ReadWriteLocker } from './ReadWriteLocker';
|
||||
import type { ResourceLocker } from './ResourceLocker';
|
||||
import type { RedisResourceLock, RedisReadWriteLock, RedisAnswer } from './scripts/RedisLuaScripts';
|
||||
@ -127,7 +128,7 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
||||
};
|
||||
}
|
||||
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
const key = this.getReadWriteKey(identifier);
|
||||
await retryFunction(
|
||||
this.swallowFalse(this.redisRw.acquireReadLock.bind(this.redisRw, key)),
|
||||
@ -143,7 +144,7 @@ export class RedisLocker implements ReadWriteLocker, ResourceLocker, Initializab
|
||||
}
|
||||
}
|
||||
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => (Promise<T> | T)): Promise<T> {
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>): Promise<T> {
|
||||
const key = this.getReadWriteKey(identifier);
|
||||
await retryFunction(
|
||||
this.swallowFalse(this.redisRw.acquireWriteLock.bind(this.redisRw, key)),
|
||||
|
@ -1,5 +1,6 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ExpiringReadWriteLocker } from './ExpiringReadWriteLocker';
|
||||
|
||||
/**
|
||||
@ -20,14 +21,14 @@ export class VoidLocker implements ExpiringReadWriteLocker {
|
||||
|
||||
public async withReadLock<T>(
|
||||
identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>,
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>,
|
||||
): Promise<T> {
|
||||
return whileLocked(noop);
|
||||
}
|
||||
|
||||
public async withWriteLock<T>(
|
||||
identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>,
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>,
|
||||
): Promise<T> {
|
||||
return whileLocked(noop);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { ResourceIdentifier } from '../../http/representation/ResourceIdentifier';
|
||||
import { getLoggerFor } from '../../logging/LogUtil';
|
||||
import { InternalServerError } from '../errors/InternalServerError';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { ExpiringReadWriteLocker } from './ExpiringReadWriteLocker';
|
||||
import type { ReadWriteLocker } from './ReadWriteLocker';
|
||||
import Timeout = NodeJS.Timeout;
|
||||
@ -24,12 +25,12 @@ export class WrappedExpiringReadWriteLocker implements ExpiringReadWriteLocker {
|
||||
}
|
||||
|
||||
public async withReadLock<T>(identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>): Promise<T> {
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>): Promise<T> {
|
||||
return this.locker.withReadLock(identifier, async(): Promise<T> => this.expiringPromise(identifier, whileLocked));
|
||||
}
|
||||
|
||||
public async withWriteLock<T>(identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>): Promise<T> {
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>): Promise<T> {
|
||||
return this.locker.withWriteLock(identifier, async(): Promise<T> => this.expiringPromise(identifier, whileLocked));
|
||||
}
|
||||
|
||||
@ -39,7 +40,7 @@ export class WrappedExpiringReadWriteLocker implements ExpiringReadWriteLocker {
|
||||
* it receives. The ResourceIdentifier is only used for logging.
|
||||
*/
|
||||
private async expiringPromise<T>(identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>): Promise<T> {
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>): Promise<T> {
|
||||
let timer: Timeout;
|
||||
let createTimeout: () => Timeout;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { resolvePromiseOrValue } from '../PromiseUtil';
|
||||
import type { PromiseOrValue } from '../PromiseUtil';
|
||||
import type { SetMultiMap } from './SetMultiMap';
|
||||
|
||||
export type MapKey<T> = T extends Map<infer TKey, any> ? TKey : never;
|
||||
@ -42,18 +44,33 @@ export function modify<T extends SetMultiMap<any, any>>(map: T, options: ModifyO
|
||||
|
||||
/**
|
||||
* Finds the result of calling `map.get(key)`.
|
||||
* If there is no result, it instead returns the default value.
|
||||
* If there is no result, it instead returns the result of the default function.
|
||||
* The Map will also be updated to assign that default value to the given key.
|
||||
*
|
||||
* @param map - Map to use.
|
||||
* @param key - Key to find the value for.
|
||||
* @param defaultValue - Value to insert and return if no result was found.
|
||||
* @param defaultFn - Function to generate default value to insert and return if no result was found.
|
||||
*/
|
||||
export function getDefault<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, defaultValue: TValue): TValue {
|
||||
export function getDefault<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, defaultFn: () => TValue): TValue;
|
||||
/**
|
||||
* Finds the result of calling `map.get(key)`.
|
||||
* If there is no result, it instead returns the result of the default function.
|
||||
* The Map will also be updated to assign the resolved default value to the given key.
|
||||
*
|
||||
* @param map - Map to use.
|
||||
* @param key - Key to find the value for.
|
||||
* @param defaultFn - Function to generate default value to insert and return if no result was found.
|
||||
*/
|
||||
export function getDefault<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, defaultFn: () => Promise<TValue>):
|
||||
Promise<TValue>;
|
||||
export function getDefault<TKey, TValue>(map: Map<TKey, TValue>, key: TKey, defaultFn: () => PromiseOrValue<TValue>):
|
||||
PromiseOrValue<TValue> {
|
||||
const value = map.get(key);
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
map.set(key, defaultValue);
|
||||
return defaultValue;
|
||||
return resolvePromiseOrValue<TValue, TValue>(defaultFn(), (val): TValue => {
|
||||
map.set(key, val);
|
||||
return val;
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import type { ResourceIdentifier } from '../../../src/http/representation/Resour
|
||||
import { LockingResourceStore } from '../../../src/storage/LockingResourceStore';
|
||||
import type { ResourceStore } from '../../../src/storage/ResourceStore';
|
||||
import type { ExpiringReadWriteLocker } from '../../../src/util/locking/ExpiringReadWriteLocker';
|
||||
import type { PromiseOrValue } from '../../../src/util/PromiseUtil';
|
||||
import { guardedStreamFrom } from '../../../src/util/StreamUtil';
|
||||
import { flushPromises } from '../../util/Util';
|
||||
|
||||
@ -47,7 +48,7 @@ describe('A LockingResourceStore', (): void => {
|
||||
|
||||
locker = {
|
||||
withReadLock: jest.fn(async <T>(id: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>): Promise<T> => {
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>): Promise<T> => {
|
||||
order.push('lock read');
|
||||
try {
|
||||
// Allows simulating a timeout event
|
||||
@ -61,7 +62,7 @@ describe('A LockingResourceStore', (): void => {
|
||||
}
|
||||
}),
|
||||
withWriteLock: jest.fn(async <T>(identifier: ResourceIdentifier,
|
||||
whileLocked: (maintainLock: () => void) => T | Promise<T>): Promise<T> => {
|
||||
whileLocked: (maintainLock: () => void) => PromiseOrValue<T>): Promise<T> => {
|
||||
order.push('lock write');
|
||||
try {
|
||||
return await whileLocked(emptyFn);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import type { ResourceIdentifier } from '../../../../src/http/representation/ResourceIdentifier';
|
||||
import type { ReadWriteLocker } from '../../../../src/util/locking/ReadWriteLocker';
|
||||
import { WrappedExpiringReadWriteLocker } from '../../../../src/util/locking/WrappedExpiringReadWriteLocker';
|
||||
import type { PromiseOrValue } from '../../../../src/util/PromiseUtil';
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
@ -14,9 +15,9 @@ describe('A WrappedExpiringReadWriteLocker', (): void => {
|
||||
|
||||
beforeEach(async(): Promise<void> => {
|
||||
wrappedLocker = {
|
||||
withReadLock: jest.fn(async<T>(id: ResourceIdentifier, whileLocked: () => T | Promise<T>):
|
||||
withReadLock: jest.fn(async<T>(id: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>):
|
||||
Promise<T> => whileLocked()),
|
||||
withWriteLock: jest.fn(async<T>(id: ResourceIdentifier, whileLocked: () => T | Promise<T>):
|
||||
withWriteLock: jest.fn(async<T>(id: ResourceIdentifier, whileLocked: () => PromiseOrValue<T>):
|
||||
Promise<T> => whileLocked()),
|
||||
};
|
||||
|
||||
|
@ -44,12 +44,17 @@ describe('MapUtil', (): void => {
|
||||
describe('#getDefault', (): void => {
|
||||
it('returns the value it finds in the Map for the given key.', async(): Promise<void> => {
|
||||
const map = new Map([[ key1, 123 ]]);
|
||||
expect(getDefault(map, key1, 999)).toBe(123);
|
||||
expect(getDefault(map, key1, (): number => 999)).toBe(123);
|
||||
});
|
||||
|
||||
it('returns the default value if it finds no value for the given key.', async(): Promise<void> => {
|
||||
const map = new Map([[ key1, 123 ]]);
|
||||
expect(getDefault(map, key2, 999)).toBe(999);
|
||||
expect(getDefault(map, key2, (): number => 999)).toBe(999);
|
||||
});
|
||||
|
||||
it('can handle async default functions.', async(): Promise<void> => {
|
||||
const map = new Map([[ key1, 123 ]]);
|
||||
await expect(getDefault(map, key2, async(): Promise<number> => 999)).resolves.toBe(999);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user