diff --git a/README.md b/README.md
index 8e1633d..3476862 100644
--- a/README.md
+++ b/README.md
@@ -44,7 +44,6 @@ $ yarn add @myunisoft/redis
- Abstraction
- [KVPeer](./docs/KVPeer.md)
- [TimedKVPeer](./docs/TimedKVPeer.md)
- - [RestrictedKV](./docs/RestrictedKV.md)
- [StoreContext](./docs/StoreContext.md)
- [PubSub](./docs/pubsub/Channel.md)
- [Stream](./docs/stream/Stream.md)
diff --git a/docs/RestrictedKV.md b/docs/RestrictedKV.md
deleted file mode 100644
index b0359ad..0000000
--- a/docs/RestrictedKV.md
+++ /dev/null
@@ -1,107 +0,0 @@
-
- RestrictedKV
-
-
-
- This class is used to prevent from brut force attack. It allow to lock key usage on a number of failed attempt.
-
-
-
-## Interface
-
-```ts
-type RestrictedKVOptions = {
- autoClearExpired?: number;
- allowedAttempt?: number;
- banTimeInSecond?: number;
-}
-
-interface Attempt {
- failure: number;
- lastTry: number;
- locked: boolean;
-}
-
-type KeyType = string | Buffer;
-```
-
-## Constants
-
-- kDefaultAllowedAttempt = 6;
-- kDefaultBanTime = 60 * 5;
-
-## 📚 Usage
-
-```ts
-import { RestrictedKV, MemoryAdapter } from "@myunisoft/redis";
-
-const allowedAttempt = 2;
-const banTime = 60;
-
-const memoryAdapter = new MemoryAdapter();
-
-const restrictedKV = new RestrictedKV({
- adapter: memoryAdapter,
- allowedAttempt,
- banTimeInSecond: banTime
-});
-```
-
-## 📜 API
-
-### getAttempt(key: KeyType): Promise< Attempt >
-
-Returns the number of attempts (failure, last tentative timestamp ...) for a given key.
-
-```ts
-const key: string = "foo"
-
-const attempt = await restrictedKV.getAttempt(key);
-const { failure, lastTry, locked } = attempt;
-
-strictEqual(failure, 0);
-strictEqual(lastTry, Date.now())
-strictEqual(locked, false);
-```
-
-### fail(key: KeyType): Promise< Attempt >
-
-Increment an attempt failure for a given key.
-When the number of failures exceeds the defined limitation, the key is locked.
-
-```ts
-const key: string = "foo";
-
-const attempt = await restrictedKV.fail(key);
-const { failure, lastTry, locked } = attempt;
-
-strictEqual(failure, 1);
-strictEqual(lastTry, Date.now());
-strictEqual(locked, false);
-```
-### success( )
-
-Notify a successful attempt for a given key. This will remove all traces of previous failed attempt.
-
-```ts
-const key: string = "foo";
-
-await restrictedKV.success(email);
-
-const attempt = await restrictedKV.getAttempt(key);
-const { failure, lastTry, locked } = attempt;
-
-strictEqual(failure, 0);
-strictEqual(lastTry, Date.now());
-strictEqual(locked, false);
-```
-
-### clearExpired()
-
-Clear all keys where the last attempt exceeds an allocated lifetime.
-
-```ts
-await restrictedKV.clearExpired()
-```
-
-Cast the event `expiredKeys` olding the removed keys.
diff --git a/docs/adapter/memory.adapter.md b/docs/adapter/memory.adapter.md
index 9675055..677a843 100644
--- a/docs/adapter/memory.adapter.md
+++ b/docs/adapter/memory.adapter.md
@@ -56,16 +56,6 @@ const result = await memoryAdapter.deleteValue(key);
console.log(result); // 0 for Failure, 1 for Success
```
-### clearExpired(options: { banTimeInSecond: number; }): (string | Buffer)[]
-
-this method is used to clear expired key-value pairs in memory
-
-```ts
-const result = await memoryAdapter.clearExpired({ banTimeInSecond: 10 });
-
-console.log(result); // []
-```
-
### getValue(key: string): null | unknown
this method is used to get a value from memory
diff --git a/docs/adapter/redis.adapter.md b/docs/adapter/redis.adapter.md
index 5718493..5ef1d6e 100644
--- a/docs/adapter/redis.adapter.md
+++ b/docs/adapter/redis.adapter.md
@@ -93,28 +93,6 @@ const result = await redisAdapter.deleteValue(key);
console.log(result); // 0 for Failure, 1 for Success
```
-### clearExpired(options: ClearExpiredOptions): (string | Buffer)[]
-
-this method is used to clear expired key-value pairs in redis
-
-```ts
-const result = await redisAdapter.clearExpired({ banTimeInSecond: 10 });
-
-console.log(result); // []
-```
-
-### isKeyExpired(options: RedisIsKeyExpiredOptions): boolean
-
-this method is used to check if a key is expired in redis
-
-```ts
-const key = "foo";
-
-const result = await redisAdapter.isKeyExpired({ key, banTimeInSecond: 10 });
-
-console.log(result); // false
-```
-
### deepParseInput(input: Record | any[]): Generator | any[]>
this method is used to deep parse input
diff --git a/src/class/RestrictedKV.class.ts b/src/class/RestrictedKV.class.ts
deleted file mode 100644
index f8fdeb1..0000000
--- a/src/class/RestrictedKV.class.ts
+++ /dev/null
@@ -1,155 +0,0 @@
-// Import Internal dependencies
-import { KVPeer, type KVOptions } from "./KVPeer.class.js";
-import type { KeyType } from "../types/index.js";
-import { ClearExpiredOptions } from "./adapter/redis.adapter.js";
-
-// CONSTANTS
-const kDefaultAllowedAttempt = 6;
-const kDefaultBanTime = 60 * 5;
-
-export type RestrictedKVOptions = Pick, "adapter"> & {
- autoClearExpired?: number;
- allowedAttempt?: number;
- banTimeInSecond?: number;
-};
-
-// type Definition
-export interface Attempt {
- failure: number;
- lastTry: number;
- locked: boolean;
-}
-
-export type RawAttempt = Record;
-
-/**
-* @class RestrictedKV
-* @classdesc Implementation to prevent brute force attacks.
-*/
-export class RestrictedKV extends KVPeer> {
- private autoClearInterval: NodeJS.Timeout | null;
-
- protected allowedAttempt: number;
- protected banTimeInSecond: number;
-
- static getDefaultAttempt() {
- return { failure: 0, lastTry: Date.now(), locked: false };
- }
-
- constructor(options: RestrictedKVOptions) {
- const { autoClearExpired, allowedAttempt, banTimeInSecond, adapter } = options;
-
- super({
- adapter,
- type: "object"
- });
-
- this.allowedAttempt = allowedAttempt ?? kDefaultAllowedAttempt;
- this.banTimeInSecond = banTimeInSecond ?? kDefaultBanTime;
-
- if (autoClearExpired) {
- this.autoClearInterval = setInterval(async() => {
- try {
- const connectionPerf = this.adapter.getPerformance ? (await this.adapter.getPerformance()) : { isAlive: true };
-
- if (connectionPerf.isAlive) {
- await this.adapter.clearExpired({
- banTimeInSecond: this.banTimeInSecond
- });
- }
- }
- catch (error) {
- console.error(error);
- }
- }, autoClearExpired).unref();
- }
- }
-
- private parseRawAttempt(data: RawAttempt): Attempt {
- return {
- failure: Number(data.failure ?? 0),
- lastTry: Number(data.lastTry ?? Date.now()),
- locked: (data.locked ?? "false") === "true"
- };
- }
-
- async clearExpired(
- options: ClearExpiredOptions = { banTimeInSecond: this.banTimeInSecond }
- ): Promise {
- const expiredKeys = await this.adapter.clearExpired(options);
-
- if (expiredKeys.length > 0) {
- this.emit("expiredKeys", expiredKeys);
- }
- }
-
- clearAutoClearInterval() {
- if (this.autoClearInterval) {
- clearInterval(this.autoClearInterval);
- }
- this.autoClearInterval = null;
- }
-
- /**
- * @description Returns the number of attempts (failure, last tentative timestamp ...) for a given key
- *
- * @param key - key
- *
- * @example
- * ```ts
- * handler.getAttempt("myKey")
- * ```
- */
- async getAttempt(key: KeyType): Promise {
- const data = await this.getValue(key) as RawAttempt | null;
-
- return Object.assign({}, RestrictedKV.getDefaultAttempt(), data === null ? {} : this.parseRawAttempt(data));
- }
-
- /**
- * @description Increment an access failure for a given key.
- * The method also allows to define whether a key is locked or not (when the number of failures exceeds the defined limitation).
- *
- * @param key - key
- *
- * @example
- * ```ts
- * handler.fail("myKey")
- * ```
- */
- async fail(key: KeyType): Promise {
- const stored = await this.getAttempt(key);
- const attempt: Attempt = { failure: 1, lastTry: Date.now(), locked: false };
-
- if (stored !== null) {
- const diff = (Date.now() - stored.lastTry) / 1000;
- if (diff < this.banTimeInSecond) {
- attempt.failure = stored.failure + 1;
- }
- if (attempt.failure > this.allowedAttempt) {
- attempt.locked = true;
- }
- }
-
- await this.adapter.setValue({ key, value: attempt });
-
- return attempt;
- }
-
- /**
- * @description Notify a successful access for a given key. This will remove all traces of previous failed access.
- *
- * @param key - key
- *
- * @example
- * ```ts
- * handler.success("email@domain.com")
- * ```
- */
- async success(key: KeyType) {
- const rawStored = await this.getValue(key);
- if (rawStored !== null) {
- await this.adapter.deleteValue(key);
- }
- }
-}
diff --git a/src/class/adapter/memory.adapter.ts b/src/class/adapter/memory.adapter.ts
index e8ef639..27b8388 100644
--- a/src/class/adapter/memory.adapter.ts
+++ b/src/class/adapter/memory.adapter.ts
@@ -47,24 +47,6 @@ export class MemoryAdapter implements DatabaseConnection {
return isDelete ? 1 : 0;
}
- clearExpired(options: { banTimeInSecond: number; }): (string | Buffer)[] {
- const { banTimeInSecond } = options;
-
- const expired: string[] = [];
-
- for (const [key, value] of this.#values) {
- if (this.isKeyExpired({
- banTimeInSecond,
- value: value as Record
- })) {
- expired.push(key);
- this.#values.delete(key);
- }
- }
-
- return expired;
- }
-
// Implement the no-argument version of getValue
getValue(key: string): null | unknown {
const valueExist = this.#values.has(key);
@@ -75,12 +57,4 @@ export class MemoryAdapter implements DatabaseConnection {
return this.#values.get(key);
}
-
- private isKeyExpired(options: InMemIsKeyExpiredOptions) {
- const { banTimeInSecond, value } = options;
-
- const lastTry = "lastTry" in value ? Number(value.lastTry) : null;
-
- return lastTry === null ? false : (Date.now() - lastTry) / 1000 >= banTimeInSecond;
- }
}
diff --git a/src/class/adapter/redis.adapter.ts b/src/class/adapter/redis.adapter.ts
index 918c1fc..c6abff0 100644
--- a/src/class/adapter/redis.adapter.ts
+++ b/src/class/adapter/redis.adapter.ts
@@ -8,7 +8,6 @@ import { Err, Ok, Result } from "@openally/result";
// Import Internal Dependencies
import { AssertConnectionError, AssertDisconnectionError } from "../error/connection.error.js";
import type { DatabaseConnection, KeyType, Value } from "../../types/index.js";
-import { Attempt } from "../RestrictedKV.class.js";
// CONSTANTS
const kDefaultAttempt = 4;
@@ -151,50 +150,6 @@ export class RedisAdapter extends Redis implements DatabaseConnection {
return this.del(finalKey);
}
- async clearExpired(options: ClearExpiredOptions): Promise<(string | Buffer)[]> {
- const promises = [this.keysBuffer("*"), this.keys("*")];
-
- const data = [...await Promise.all(promises)].flat();
- if (data.length === 0) {
- return [];
- }
-
- const results = await Promise.all(data.map(async(key) => {
- const expired = await this.isKeyExpired({
- ...options,
- key
- });
-
- return { key, expired };
- }));
-
- const expiredKeys = results
- .filter((row) => row.expired)
- .map((row) => row.key);
-
- if (expiredKeys.length > 0) {
- const pipeline = this.pipeline();
-
- expiredKeys.forEach((key) => pipeline.del(key));
-
- await pipeline.exec();
- }
-
- return expiredKeys;
- }
-
- private async isKeyExpired(options: RedisIsKeyExpiredOptions): Promise {
- const { banTimeInSecond, key } = options;
-
- const attempt = await this.getValue(
- key,
- "object"
- ) as Attempt;
- const lastTry = "lastTry" in attempt ? Number(attempt.lastTry) : null;
-
- return lastTry === null ? false : (Date.now() - lastTry) / 1000 >= banTimeInSecond;
- }
-
private* deepParseInput(input: Record | any[]) {
if (Array.isArray(input)) {
for (const value of input) {
diff --git a/src/index.ts b/src/index.ts
index 0ef554e..f1ee7e2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,7 +7,6 @@ export * from "./class/stream/index.js";
export * from "./class/pubSub/Channel.class.js";
export * from "./class/KVPeer.class.js";
export * from "./class/TimedKVPeer.class.js";
-export * from "./class/RestrictedKV.class.js";
export * from "./class/StoreContext.class.js";
// Export Types
diff --git a/src/types/index.ts b/src/types/index.ts
index 7c22779..5c36d6b 100644
--- a/src/types/index.ts
+++ b/src/types/index.ts
@@ -47,6 +47,5 @@ export interface DatabaseConnection {
setValue(...unknown): Promise> | Result;
deleteValue(...unknown): Promise | number;
- clearExpired(...unknown): Promise<(string | Buffer)[]> | (string | Buffer)[];
getValue(...unknown): Promise | unknown;
}
diff --git a/test/class/RestrictedKV.spec.ts b/test/class/RestrictedKV.spec.ts
deleted file mode 100644
index c6b889f..0000000
--- a/test/class/RestrictedKV.spec.ts
+++ /dev/null
@@ -1,612 +0,0 @@
-/* eslint-disable max-nested-callbacks */
-// Import Node.js Dependencies
-import assert from "node:assert";
-import { describe, before, after, test, it, beforeEach, mock, Mock } from "node:test";
-import timers from "node:timers/promises";
-import { EventEmitter } from "node:events";
-
-// Import Third-party Dependencies
-import MockDate from "mockdate";
-
-// Import Internal Dependencies
-import { MemoryAdapter, RedisAdapter, RestrictedKV } from "../../src";
-import { randomValue } from "../fixtures/utils/randomValue";
-
-// Internal Dependencies Mock
-mock.method(RestrictedKV.prototype, "deleteValue", async() => "deleteValue");
-
-MockDate.set(Date.now());
-
-describe("RestrictedKV", () => {
- describe("RedisAdapter", () => {
- let redisAdapter: RedisAdapter;
-
- before(async() => {
- redisAdapter = new RedisAdapter({
- port: Number(process.env.REDIS_PORT),
- host: process.env.REDIS_HOST
- });
-
- await redisAdapter.initialize();
- await redisAdapter.flushdb();
- });
-
- after(async() => {
- await redisAdapter.close(true);
- });
-
- describe("Instantiated with default options", () => {
- let restrictedKV: RestrictedKV;
-
- before(() => {
- restrictedKV = new RestrictedKV({
- adapter: redisAdapter
- });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test(`WHEN calling getDefaultAttempt
- THEN it should return a default default attempt object`, () => {
- const defaultAttempt = RestrictedKV.getDefaultAttempt();
- assert.deepStrictEqual(defaultAttempt, {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
-
- describe("getAttempt", () => {
- const lastTry = Date.now();
-
- test(`Given an Attempt object with all keys
- WHEN calling getAttempt
- THEN it should return the initial object`,
- async() => {
- const key = randomValue();
- const payload = { failure: 0, lastTry, locked: false };
-
- await restrictedKV.setValue({ key, value: payload });
- assert.deepEqual(await restrictedKV.getAttempt(key), payload);
- });
-
- test(`Given a partial of Attempt object
- WHEN calling getAttempt
- THEN it should return a completed Attempt object`,
- async() => {
- const optionsWithLocked = {
- key: randomValue(),
- value: { locked: false }
- };
-
- await restrictedKV.setValue(optionsWithLocked);
- const attemptWithLocked = await restrictedKV.getAttempt(optionsWithLocked.key);
- assert.deepEqual(attemptWithLocked, Object.assign({},
- { lastTry: Date.now(), failure: 0 },
- optionsWithLocked.value
- ));
-
- const optionsWithFailure = {
- key: randomValue(),
- value: { failure: 0 }
- };
-
- await restrictedKV.setValue(optionsWithFailure);
- const attemptWithFailure = await restrictedKV.getAttempt(optionsWithFailure.key);
- assert.deepEqual(attemptWithFailure, Object.assign({},
- { lastTry: Date.now(), locked: false },
- optionsWithFailure.value
- ));
- });
- });
-
- describe("fail", () => {
- test(`Given a fake key
- WHEN calling fail
- THEN it should return a new Attempt object with failure property init at 1`,
- async() => {
- const attempt = await restrictedKV.fail("my-fake-key");
- assert.equal(attempt.failure, 1);
- });
-
- test(`Given a valid key
- WHEN calling fail
- THEN it should return the associated Attempt object with failure property incremented`,
- async() => {
- const lastTry = Date.now();
- const payload = { failure: 1, lastTry, locked: false };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- const attempt = await restrictedKV.fail(key);
- assert.equal(attempt.failure, payload.failure + 1);
- });
- });
-
- describe("success", () => {
- let deleteValueMock: Mock;
-
- before(() => {
- deleteValueMock = mock.method(redisAdapter, "deleteValue", async() => 1);
- });
-
- after(() => {
- deleteValueMock.mock.restore();
- });
-
- test(`GIVEN an unknown key
- WHEN calling success
- THEN it should return a clean Attempt object`,
- async() => {
- const key = randomValue();
-
- await restrictedKV.success(key);
-
- const attempt = await restrictedKV.getAttempt(key);
- assert.deepEqual(attempt, {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
-
- test(`GIVEN a known key
- WHEN calling success
- THEN it should deleted the KVPeer`,
- async() => {
- const lastTry = Date.now();
- const payload = { failure: 0, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
- await restrictedKV.success(key);
-
- assert.equal(deleteValueMock.mock.calls.length, 1);
- assert.deepEqual(deleteValueMock.mock.calls[0].arguments, [key]);
- });
- });
-
- describe("clearExpired", () => {
- test("should not clean not expired key from database", async() => {
- const payload = { failure: 0, lastTry: Date.now(), locked: false };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
- await restrictedKV.clearExpired();
-
- assert.deepEqual(await restrictedKV.getAttempt(key), payload);
- });
-
- test("should clear all keys from the database when invoked", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
- await restrictedKV.clearExpired();
-
- const attempt = await restrictedKV.getAttempt(key);
- assert.equal(attempt.failure, 0);
- assert.equal(attempt.locked, false);
- });
-
- describe("expiredKeys event", () => {
- let emitMock: Mock;
- before(() => {
- emitMock = mock.method(RestrictedKV.prototype, "emit", () => void 0);
- });
-
- test("should not send an event when no cleared key", async() => {
- await restrictedKV.clearExpired();
-
- assert.equal(emitMock.mock.calls.length, 0);
- });
-
- test("should send an event with expiredKeys", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- await restrictedKV.clearExpired();
- assert.equal(emitMock.mock.calls.length, 1);
- });
- });
- });
- });
-
- describe("allowedAttempt", () => {
- const allowedAttempt = 2;
-
- let restrictedKV: RestrictedKV;
-
- before(() => {
- restrictedKV = new RestrictedKV({
- allowedAttempt,
- adapter: redisAdapter
- });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test("should lock the key after allowedAttempt fail instead of 3", async() => {
- const lastTry = Date.now();
- const payload = { failure: 2, lastTry, locked: false };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- const attempt = await restrictedKV.fail(key);
- assert.equal(attempt.failure, payload.failure + 1);
- assert.equal(attempt.locked, true);
- });
- });
-
- describe("banTime", () => {
- const allowedAttempt = 2;
- const banTime = 60;
-
- const key = randomValue();
- const lastTry = Date.now();
- const payload = { failure: 1, lastTry, locked: false };
-
- let restrictedKV: RestrictedKV;
-
- before(async() => {
- restrictedKV = new RestrictedKV({
- allowedAttempt,
- banTimeInSecond: banTime,
- adapter: redisAdapter
- });
-
- await restrictedKV.setValue({ key, value: payload });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test("should unlock the key after the given banTime", async() => {
- await timers.setTimeout(140);
-
- const attempt = await restrictedKV.getValue(key);
- assert.equal(attempt?.failure, payload.failure);
- });
- });
-
- describe("autoClearInterval database suite", () => {
- let restrictedKV: RestrictedKV;
-
- before(async() => {
- restrictedKV = new RestrictedKV({
- autoClearExpired: 20,
- adapter: redisAdapter
- });
- });
-
- beforeEach(async() => {
- await redisAdapter.flushdb();
- });
-
- after(async() => {
- restrictedKV.clearAutoClearInterval();
- });
-
- it("should clear all keys from the database when invoked", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- await timers.setTimeout(1000);
-
- assert.deepEqual(await restrictedKV.getAttempt(key), {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
- });
- });
-
- describe("MemoryAdapter", () => {
- let memoryAdapter: MemoryAdapter;
-
- before(async() => {
- memoryAdapter = new MemoryAdapter();
- });
-
- describe("Instantiated with default options", () => {
- let restrictedKV: RestrictedKV;
-
- before(() => {
- restrictedKV = new RestrictedKV({
- adapter: memoryAdapter
- });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test(`WHEN calling getDefaultAttempt
- THEN it should return a default default attempt object`, () => {
- const defaultAttempt = RestrictedKV.getDefaultAttempt();
- assert.deepStrictEqual(defaultAttempt, {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
-
- describe("getAttempt", () => {
- const lastTry = Date.now();
-
- test(`Given an Attempt object with all keys
- WHEN calling getAttempt
- THEN it should return the initial object`,
- async() => {
- const key = randomValue();
- const payload = { failure: 0, lastTry, locked: false };
-
- await restrictedKV.setValue({ key, value: payload });
- assert.deepEqual(await restrictedKV.getAttempt(key), payload);
- });
-
- test(`Given a partial of Attempt object
- WHEN calling getAttempt
- THEN it should return a completed Attempt object`,
- async() => {
- const optionsWithLocked = {
- key: randomValue(),
- value: { locked: false }
- };
-
- await restrictedKV.setValue(optionsWithLocked);
- const attemptWithLocked = await restrictedKV.getAttempt(optionsWithLocked.key);
- assert.deepEqual(attemptWithLocked, Object.assign({},
- { lastTry: Date.now(), failure: 0 },
- optionsWithLocked.value
- ));
-
- const optionsWithFailure = {
- key: randomValue(),
- value: { failure: 0 }
- };
-
- await restrictedKV.setValue(optionsWithFailure);
- const attemptWithFailure = await restrictedKV.getAttempt(optionsWithFailure.key);
- assert.deepEqual(attemptWithFailure, Object.assign({},
- { lastTry: Date.now(), locked: false },
- optionsWithFailure.value
- ));
- });
- });
-
- describe("fail", () => {
- test(`Given a fake key
- WHEN calling fail
- THEN it should return a new Attempt object with failure property init at 1`,
- async() => {
- const attempt = await restrictedKV.fail("my-fake-key");
- assert.equal(attempt.failure, 1);
- });
-
- test(`Given a valid key
- WHEN calling fail
- THEN it should return the associated Attempt object with failure property incremented`,
- async() => {
- const lastTry = Date.now();
- const payload = { failure: 1, lastTry, locked: false };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- const attempt = await restrictedKV.fail(key);
- assert.equal(attempt.failure, payload.failure + 1);
- });
- });
-
- describe("success", () => {
- let deleteValueMock: Mock;
-
- before(() => {
- deleteValueMock = mock.method(memoryAdapter, "deleteValue", async() => 1);
- });
-
- after(() => {
- deleteValueMock.mock.restore();
- });
-
- test(`GIVEN an unknown key
- WHEN calling success
- THEN it should return a clean Attempt object`,
- async() => {
- const key = randomValue();
-
- await restrictedKV.success(key);
-
- const attempt = await restrictedKV.getAttempt(key);
- assert.deepEqual(attempt, {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
-
- test(`GIVEN a known key
- WHEN calling success
- THEN it should deleted the KVPeer`,
- async() => {
- const lastTry = Date.now();
- const payload = { failure: 0, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
- await restrictedKV.success(key);
-
- assert.equal(deleteValueMock.mock.calls.length, 1);
- assert.deepEqual(deleteValueMock.mock.calls[0].arguments, [key]);
- });
- });
-
- describe("clearExpired", () => {
- // test("should not clean not expired key from database", async() => {
- // const payload = { failure: 0, lastTry: Date.now(), locked: false };
- // const key = randomValue();
-
- // await restrictedKV.setValue({ key, value: payload });
- // await restrictedKV.clearExpired();
-
- // assert.deepEqual(await restrictedKV.getAttempt(key), payload);
- // });
-
- test("should clear all keys from the database when invoked", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
- await restrictedKV.clearExpired();
-
- const attempt = await restrictedKV.getAttempt(key);
- assert.equal(attempt.failure, 0);
- assert.equal(attempt.locked, false);
- });
-
- describe("expiredKeys event", () => {
- let emitMock: Mock;
- before(() => {
- emitMock = mock.method(RestrictedKV.prototype, "emit", () => void 0);
- });
-
- test("should not send an event when no cleared key", async() => {
- await restrictedKV.clearExpired();
-
- assert.equal(emitMock.mock.calls.length, 0);
- });
-
- test("should send an event with expiredKeys", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- await restrictedKV.clearExpired();
- assert.equal(emitMock.mock.calls.length, 1);
- });
- });
- });
- });
-
- describe("allowedAttempt", () => {
- const allowedAttempt = 2;
-
- let restrictedKV: RestrictedKV;
-
- before(() => {
- restrictedKV = new RestrictedKV({
- allowedAttempt,
- adapter: memoryAdapter
- });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test("should lock the key after allowedAttempt fail instead of 3", async() => {
- const lastTry = Date.now();
- const payload = { failure: 2, lastTry, locked: false };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- const attempt = await restrictedKV.fail(key);
- assert.equal(attempt.failure, payload.failure + 1);
- assert.equal(attempt.locked, true);
- });
- });
-
- describe("banTime", () => {
- const allowedAttempt = 2;
- const banTime = 60;
-
- const key = randomValue();
- const lastTry = Date.now();
- const payload = { failure: 1, lastTry, locked: false };
-
- let restrictedKV: RestrictedKV;
-
- before(async() => {
- restrictedKV = new RestrictedKV({
- allowedAttempt,
- banTimeInSecond: banTime,
- adapter: memoryAdapter
- });
-
- await restrictedKV.setValue({ key, value: payload });
- });
-
- it("should be instantiated", () => {
- assert.ok(restrictedKV instanceof RestrictedKV);
- assert.ok(restrictedKV instanceof EventEmitter);
- });
-
- test("should unlock the key after the given banTime", async() => {
- await timers.setTimeout(140);
-
- const attempt = await restrictedKV.getValue(key);
- assert.equal(attempt?.failure, payload.failure);
- });
- });
-
- describe("autoClearInterval database suite", () => {
- let restrictedKV: RestrictedKV;
-
- before(async() => {
- restrictedKV = new RestrictedKV({
- autoClearExpired: 20,
- adapter: memoryAdapter
- });
- });
-
- beforeEach(async() => {
- memoryAdapter.flushall();
- });
-
- after(async() => {
- restrictedKV.clearAutoClearInterval();
- });
-
- it("should clear all keys from the database when invoked", async() => {
- const lastTry = Date.now() - (90 * 1_000 * 60);
- const payload = { failure: 3, lastTry, locked: true };
- const key = randomValue();
-
- await restrictedKV.setValue({ key, value: payload });
-
- await timers.setTimeout(1000);
-
- assert.deepEqual(await restrictedKV.getAttempt(key), {
- failure: 0,
- locked: false,
- lastTry: Date.now()
- });
- });
- });
- });
-});
diff --git a/test/class/adapter/memory.adapter.spec.ts b/test/class/adapter/memory.adapter.spec.ts
index 71285da..a7d2adb 100644
--- a/test/class/adapter/memory.adapter.spec.ts
+++ b/test/class/adapter/memory.adapter.spec.ts
@@ -59,23 +59,6 @@ describe("MemoryAdapter", () => {
});
});
- describe("clearExpired", () => {
- const key = "foo";
- const value = { value: "bar", lastTry: Date.now() };
-
- const memoryAdapter = new MemoryAdapter();
-
- it("Should clear expired key for the given banTimeInSecond", async() => {
- memoryAdapter.setValue({ key, value });
-
- await timers.setTimeout(100);
-
- const result = memoryAdapter.clearExpired({ banTimeInSecond: 0 });
-
- assert.equal(result[0], key);
- });
- });
-
describe("getValue", () => {
const key = "foo";
const fakeKey = "fake";