210 lines
No EOL
6.4 KiB
TypeScript
210 lines
No EOL
6.4 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'bun:test';
|
|
import { RedisCache } from '../src/redis-cache';
|
|
import type { CacheOptions } from '../src/types';
|
|
|
|
describe('RedisCache', () => {
|
|
let cache: RedisCache;
|
|
|
|
beforeEach(() => {
|
|
const options: CacheOptions = {
|
|
keyPrefix: 'test:',
|
|
ttl: 3600,
|
|
redisConfig: { host: 'localhost', port: 6379 },
|
|
};
|
|
cache = new RedisCache(options);
|
|
});
|
|
|
|
describe('Core functionality', () => {
|
|
it('should create cache instance', () => {
|
|
expect(cache).toBeDefined();
|
|
expect(cache.isReady).toBeDefined();
|
|
expect(cache.get).toBeDefined();
|
|
expect(cache.set).toBeDefined();
|
|
});
|
|
|
|
it('should have stats tracking', () => {
|
|
const stats = cache.getStats();
|
|
expect(stats).toMatchObject({
|
|
hits: 0,
|
|
misses: 0,
|
|
errors: 0,
|
|
hitRate: 0,
|
|
total: 0,
|
|
});
|
|
expect(stats.uptime).toBeGreaterThanOrEqual(0);
|
|
});
|
|
});
|
|
|
|
describe('Basic operations', () => {
|
|
it('should handle get/set operations', async () => {
|
|
const key = 'test-key';
|
|
const value = { foo: 'bar' };
|
|
|
|
// Should return null for non-existent key
|
|
const miss = await cache.get(key);
|
|
expect(miss).toBeNull();
|
|
|
|
// Should set and retrieve value
|
|
await cache.set(key, value);
|
|
const retrieved = await cache.get(key);
|
|
expect(retrieved).toEqual(value);
|
|
|
|
// Should delete key
|
|
await cache.del(key);
|
|
const deleted = await cache.get(key);
|
|
expect(deleted).toBeNull();
|
|
});
|
|
|
|
it('should check key existence', async () => {
|
|
const key = 'existence-test';
|
|
|
|
expect(await cache.exists(key)).toBe(false);
|
|
|
|
await cache.set(key, 'value');
|
|
expect(await cache.exists(key)).toBe(true);
|
|
|
|
await cache.del(key);
|
|
expect(await cache.exists(key)).toBe(false);
|
|
});
|
|
|
|
it('should handle TTL in set operations', async () => {
|
|
const key = 'ttl-test';
|
|
const value = 'test-value';
|
|
|
|
// Set with custom TTL as number
|
|
await cache.set(key, value, 1);
|
|
expect(await cache.get(key)).toBe(value);
|
|
|
|
// Set with custom TTL in options
|
|
await cache.set(key, value, { ttl: 2 });
|
|
expect(await cache.get(key)).toBe(value);
|
|
});
|
|
});
|
|
|
|
describe('Advanced set options', () => {
|
|
it('should handle onlyIfExists option', async () => {
|
|
const key = 'conditional-test';
|
|
const value1 = 'value1';
|
|
const value2 = 'value2';
|
|
|
|
// Should not set if key doesn't exist
|
|
await cache.set(key, value1, { onlyIfExists: true });
|
|
expect(await cache.get(key)).toBeNull();
|
|
|
|
// Create the key
|
|
await cache.set(key, value1);
|
|
|
|
// Should update if key exists
|
|
await cache.set(key, value2, { onlyIfExists: true });
|
|
expect(await cache.get(key)).toBe(value2);
|
|
});
|
|
|
|
it('should handle onlyIfNotExists option', async () => {
|
|
const key = 'nx-test';
|
|
const value1 = 'value1';
|
|
const value2 = 'value2';
|
|
|
|
// Should set if key doesn't exist
|
|
await cache.set(key, value1, { onlyIfNotExists: true });
|
|
expect(await cache.get(key)).toBe(value1);
|
|
|
|
// Should not update if key exists
|
|
await cache.set(key, value2, { onlyIfNotExists: true });
|
|
expect(await cache.get(key)).toBe(value1);
|
|
});
|
|
|
|
it('should handle preserveTTL option', async () => {
|
|
const key = 'preserve-ttl-test';
|
|
const value1 = 'value1';
|
|
const value2 = 'value2';
|
|
|
|
// Set with short TTL
|
|
await cache.set(key, value1, 10);
|
|
|
|
// Update preserving TTL
|
|
await cache.set(key, value2, { preserveTTL: true });
|
|
expect(await cache.get(key)).toBe(value2);
|
|
});
|
|
|
|
it('should handle getOldValue option', async () => {
|
|
const key = 'old-value-test';
|
|
const value1 = 'value1';
|
|
const value2 = 'value2';
|
|
|
|
// Should return null when no old value
|
|
const oldValue1 = await cache.set(key, value1, { getOldValue: true });
|
|
expect(oldValue1).toBeNull();
|
|
|
|
// Should return old value
|
|
const oldValue2 = await cache.set(key, value2, { getOldValue: true });
|
|
expect(oldValue2).toBe(value1);
|
|
});
|
|
});
|
|
|
|
describe('Error handling', () => {
|
|
it('should handle errors gracefully in get', async () => {
|
|
// Force an error by using invalid JSON
|
|
const badCache = new RedisCache({
|
|
keyPrefix: 'bad:',
|
|
redisConfig: { host: 'localhost', port: 6379 },
|
|
});
|
|
|
|
// This would normally throw but should return null
|
|
const result = await badCache.get('non-existent');
|
|
expect(result).toBeNull();
|
|
|
|
// Check stats updated
|
|
const stats = badCache.getStats();
|
|
expect(stats.misses).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('Pattern operations', () => {
|
|
it('should find keys by pattern', async () => {
|
|
// Clear first to ensure clean state
|
|
await cache.clear();
|
|
|
|
await cache.set('user:1', { id: 1 });
|
|
await cache.set('user:2', { id: 2 });
|
|
await cache.set('post:1', { id: 1 });
|
|
|
|
const userKeys = await cache.keys('user:*');
|
|
expect(userKeys).toHaveLength(2);
|
|
expect(userKeys).toContain('user:1');
|
|
expect(userKeys).toContain('user:2');
|
|
|
|
const allKeys = await cache.keys('*');
|
|
expect(allKeys.length).toBeGreaterThanOrEqual(3);
|
|
expect(allKeys).toContain('user:1');
|
|
expect(allKeys).toContain('user:2');
|
|
expect(allKeys).toContain('post:1');
|
|
});
|
|
|
|
it('should clear all keys with prefix', async () => {
|
|
await cache.set('key1', 'value1');
|
|
await cache.set('key2', 'value2');
|
|
|
|
await cache.clear();
|
|
|
|
const keys = await cache.keys('*');
|
|
expect(keys).toHaveLength(0);
|
|
});
|
|
});
|
|
|
|
describe('Health checks', () => {
|
|
it('should check health', async () => {
|
|
const healthy = await cache.health();
|
|
expect(healthy).toBe(true);
|
|
});
|
|
|
|
it('should check if ready', () => {
|
|
// May not be ready immediately
|
|
const ready = cache.isReady();
|
|
expect(typeof ready).toBe('boolean');
|
|
});
|
|
|
|
it('should wait for ready', async () => {
|
|
await expect(cache.waitForReady(1000)).resolves.toBeUndefined();
|
|
});
|
|
});
|
|
}); |