diff --git a/packages/cooldown/package.json b/packages/cooldown/package.json index 660ce05..d8c6225 100644 --- a/packages/cooldown/package.json +++ b/packages/cooldown/package.json @@ -17,7 +17,8 @@ "dev": "tsc --watch", "build": "tsc", "lint": "biome lint --write ./src", - "format": "biome format --write ./src" + "format": "biome format --write ./src", + "test": "node --test ./test/*" }, "devDependencies": { "@types/node": "^22.4.0", diff --git a/packages/cooldown/src/manager.ts b/packages/cooldown/src/manager.ts index fdebfbc..21daaba 100644 --- a/packages/cooldown/src/manager.ts +++ b/packages/cooldown/src/manager.ts @@ -1,6 +1,7 @@ import type { UsingClient } from 'seyfert'; import { type CooldownData, type CooldownDataInsert, Cooldowns, type CooldownType } from './resource'; import { getMilliseconds } from './clock'; +import { fakePromise } from 'seyfert/lib/common'; export class CooldownManager { resource: Cooldowns; @@ -27,16 +28,18 @@ export class CooldownManager { const data = this.getData(name); if (!data) return false; - const cooldown = this.resource.get(`${name}:${data.type}:${target}`); - if (!cooldown) { - this.set(name, target, { type: data.type, interval: data.interval, remaining: data.refill - data.tokens }); - return false; - } + return fakePromise(this.resource.get(`${name}:${data.type}:${target}`)).then(cooldown => { + if (!cooldown) { + return fakePromise( + this.set(name, target, { type: data.type, interval: data.interval, remaining: data.refill - data.tokens }), + ).then(() => false); + } - const remaining = cooldown.remaining - data.tokens; + const remaining = cooldown.remaining - data.tokens; - if (remaining <= 0) return true; - return false; + if (remaining <= 0) return true; + return false; + }); } /** @@ -50,21 +53,22 @@ export class CooldownManager { const data = this.getData(name); if (!data) return; - const cooldown = this.resource.get(`${name}:${data.type}:${target}`); - if (!cooldown) { - this.set(name, target, { - type: data.type, - interval: data.interval, - remaining: data.refill - (tokens ?? data.tokens), + return fakePromise(this.resource.get(`${name}:${data.type}:${target}`)).then(cooldown => { + if (!cooldown) { + return fakePromise( + this.set(name, target, { + type: data.type, + interval: data.interval, + remaining: data.refill - (tokens ?? data.tokens), + }), + ).then(() => true); + } + + return fakePromise(this.drip(name, target, data, cooldown)).then(drip => { + if (drip.remaining >= data.tokens) return false; + return true; }); - return true; - } - - const drip = this.drip(name, target, data, cooldown); - - if (drip.remaining <= 0) return false; - - return true; + }); } /** @@ -79,18 +83,17 @@ export class CooldownManager { const refill = tokens ?? data.refill; - const cooldown = this.resource.get(`${name}:${data.type}:${target}`); - if (!cooldown) { - this.set(name, target, { - type: data.type, - interval: data.interval, - remaining: refill, - }); - return true; - } - - this.set(name, target, { type: data.type, interval: data.interval, remaining: refill }); - return true; + return fakePromise(this.resource.get(`${name}:${data.type}:${target}`)).then(cooldown => { + if (!cooldown) { + return fakePromise( + this.set(name, target, { type: data.type, interval: data.interval, remaining: refill }), + ).then(() => true); + } + + return fakePromise(this.set(name, target, { type: data.type, interval: data.interval, remaining: refill })).then( + () => true, + ); + }); } /** @@ -100,7 +103,7 @@ export class CooldownManager { * @param data - The cooldown data to set */ set(name: string, target: string, data: CooldownDataInsert & { type: `${CooldownType}` }) { - this.resource.set(`${name}:${data.type}:${target}`, data); + return fakePromise(this.resource.set(`${name}:${data.type}:${target}`, data)).then(() => {}); } /** @@ -119,8 +122,7 @@ export class CooldownManager { const dripAmount = deltaMS * (props.refill / props.interval); data.remaining = Math.min(data.remaining + dripAmount, props.refill); const result = { type: props.type, interval: props.interval, remaining: data.remaining }; - this.set(name, target, result); - return result; + return fakePromise(this.set(name, target, result)).then(() => result); } } diff --git a/packages/redis-adapter/package.json b/packages/redis-adapter/package.json index d2ce221..141f7d2 100644 --- a/packages/redis-adapter/package.json +++ b/packages/redis-adapter/package.json @@ -16,10 +16,11 @@ "dev": "tsc --watch", "build": "tsc", "lint": "biome lint --write ./src", - "format": "biome format --write ./src" + "format": "biome format --write ./src", + "test": "node --test ./test/*" }, "dependencies": { - "ioredis": "^5.4.1", + "iovalkey": "^0.1.0", "seyfert": "^2.0.0" }, "devDependencies": { diff --git a/packages/redis-adapter/src/adapter.ts b/packages/redis-adapter/src/adapter.ts index c4f2d32..6aa3cb7 100644 --- a/packages/redis-adapter/src/adapter.ts +++ b/packages/redis-adapter/src/adapter.ts @@ -1,6 +1,6 @@ -import type { RedisOptions } from 'ioredis'; +import type { RedisOptions } from 'iovalkey'; import type { Adapter } from 'seyfert/lib/cache'; -import { Redis } from 'ioredis'; +import { Redis } from 'iovalkey'; interface RedisAdapterOptions { namespace?: string; @@ -63,7 +63,12 @@ export class RedisAdapter implements Adapter { pipeline.hgetall(this.buildKey(key)); } - return (await pipeline.exec())?.filter(x => !!x[1]).map(x => toNormal(x[1] as Record)) ?? []; + return ( + (await pipeline.exec()) + ?.filter(x => !!x[1]) + .map(x => toNormal(x[1] as Record)) + .filter(x => x) ?? [] + ); } async get(keys: string): Promise { @@ -150,6 +155,7 @@ export class RedisAdapter implements Adapter { } async bulkRemove(keys: string[]) { + if (!keys.length) return; await this.client.del(...keys.map(x => this.buildKey(x))); } diff --git a/packages/redis-adapter/test/adapter.test.js b/packages/redis-adapter/test/adapter.test.js new file mode 100644 index 0000000..7fc1b30 --- /dev/null +++ b/packages/redis-adapter/test/adapter.test.js @@ -0,0 +1,72 @@ +const { strict } = require("node:assert/strict"); +const { test, describe, after, before } = require('node:test'); +const { RedisAdapter } = require('../lib/adapter'); + +describe('RedisAdapter', async () => { + const bulk = [['key1', { value: 'value1' }], ['key2', { value: 'value2' }]] + + const adapter = new RedisAdapter({ + redisOptions: { + host: 'localhost', + port: 6379, + }, + namespace: 'test', + }); + + before(async () => { + // Clean the Redis instance before each test + await adapter.flush(); + + }); + + await test('constructor', () => { + strict.strictEqual(adapter.isAsync, true); + strict.strictEqual(adapter.namespace, 'test'); + }); + + await test('get', async () => { + const result = await adapter.get('testKey'); + strict.deepStrictEqual(result, undefined); + }); + + await test('set', async () => { + await strict.doesNotReject(async () => { + await adapter.set('testKey', { value: 'testValue' }); + }); + }); + + await test('bulkSet', async () => { + await strict.doesNotReject(async () => { + await adapter.bulkSet(bulk); + }); + }); + + await test('bulkGet', async () => { + const result = await adapter.bulkGet(['key1', 'key2']); + strict.deepStrictEqual(result, bulk.map(x => x[1])); + }); + + + + await test('patch', async () => { + await strict.doesNotReject(async () => { + await adapter.patch(false, 'testKey', { newValue: 'updatedValue' }); + }); + }); + + await test('bulkPatch', async () => { + await strict.doesNotReject(async () => { + await adapter.bulkPatch(false, [['key1', { newValue: 'updatedValue1' }], ['key2', { newValue: 'updatedValue2' }]]); + }); + }); + + await test('scan', async () => { + const result = await adapter.scan('test*'); + strict.strictEqual(result.length, 3); + }); + + after(async () => { + await adapter.flush(); + await adapter.client.quit(); + }); +}); diff --git a/packages/redis-adapter/test/cache.test.js b/packages/redis-adapter/test/cache.test.js new file mode 100644 index 0000000..34bfa99 --- /dev/null +++ b/packages/redis-adapter/test/cache.test.js @@ -0,0 +1,37 @@ +const { describe, test, after, before } = require('node:test'); +const { Cache, Client } = require('seyfert'); +const { RedisAdapter } = require('../lib/index'); +const { doesNotReject } = require('node:assert/strict'); +const { setTimeout } = require('node:timers/promises'); +const { doesNotThrow } = require('node:assert/strict'); + +// all intents +const intents = 53608447; + +describe('Test Adapter cache', async t => { + const adapter = new RedisAdapter({ + redisOptions: { + host: 'localhost', + port: 6379, + }, + namespace: 'test_cache', + }); + + test('discord cache', async () => { + doesNotThrow(async () => { + const client = new Client({ getRC: () => ({ intents }) }); + client.setServices({ + cache: { + adapter, + } + }) + await client.cache.testAdapter(); + }); + await setTimeout(5000); + }); + + after(async () => { + await adapter.flush() + await adapter.client.quit(); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 158d6ff..48bd181 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -62,9 +62,9 @@ importers: packages/redis-adapter: dependencies: - ioredis: - specifier: ^5.4.1 - version: 5.4.1 + iovalkey: + specifier: ^0.1.0 + version: 0.1.0 seyfert: specifier: ^2.0.0 version: 2.0.0 @@ -286,8 +286,8 @@ packages: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} - ioredis@5.4.1: - resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} + iovalkey@0.1.0: + resolution: {integrity: sha512-a1RA4X99qIXSQW1AhxbOwPOvttyEzLmweVD2j6qn7SaxcF2FJ3qDbgP7fibBhLw5IQE0UZOX7+gpWRT3asOMxw==} engines: {node: '>=12.22.0'} is-binary-path@2.1.0: @@ -524,7 +524,7 @@ snapshots: dependencies: is-glob: 4.0.3 - ioredis@5.4.1: + iovalkey@0.1.0: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2