managarten/apps-archived/uload/apps/web/src/lib/cache.test.ts
Till-JS 61d181fbc2 chore: archive inactive projects to apps-archived/
Move inactive projects out of active workspace:
- bauntown (community website)
- maerchenzauber (AI story generation)
- memoro (voice memo app)
- news (news aggregation)
- nutriphi (nutrition tracking)
- reader (reading app)
- uload (URL shortener)
- wisekeep (AI wisdom extraction)

Update CLAUDE.md documentation:
- Add presi to active projects
- Document archived projects section
- Update workspace configuration

Archived apps can be re-activated by moving back to apps/

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 07:03:59 +01:00

219 lines
5.4 KiB
TypeScript

import { describe, test, expect, beforeEach, vi } from 'vitest';
import { cache, cacheKey, CacheKeys } from './cache';
describe('Cache System', () => {
beforeEach(() => {
cache.clear();
});
describe('Basic Cache Operations', () => {
test('should set and get values', () => {
const key = 'test-key';
const value = { data: 'test' };
cache.set(key, value);
const result = cache.get(key);
expect(result).toEqual(value);
});
test('should return null for non-existent keys', () => {
const result = cache.get('non-existent');
expect(result).toBeNull();
});
test('should handle TTL expiration', async () => {
const key = 'ttl-test';
const value = 'test-value';
const shortTTL = 10; // 10ms
cache.set(key, value, shortTTL);
// Should be available immediately
expect(cache.get(key)).toBe(value);
// Wait for TTL to expire
await new Promise((resolve) => setTimeout(resolve, 20));
// Should be null after expiration
expect(cache.get(key)).toBeNull();
});
test('should delete specific keys', () => {
cache.set('key1', 'value1');
cache.set('key2', 'value2');
cache.delete('key1');
expect(cache.get('key1')).toBeNull();
expect(cache.get('key2')).toBe('value2');
});
test('should clear all keys', () => {
cache.set('key1', 'value1');
cache.set('key2', 'value2');
cache.clear();
expect(cache.get('key1')).toBeNull();
expect(cache.get('key2')).toBeNull();
});
});
describe('Cache Key Generation', () => {
test('should generate cache keys correctly', () => {
const key = cacheKey('user', 123, 'profile');
expect(key).toBe('user:123:profile');
});
test('should handle different data types in keys', () => {
const key = cacheKey('prefix', 42, 'suffix', true);
expect(key).toBe('prefix:42:suffix:true');
});
test('should generate predefined cache keys', () => {
expect(CacheKeys.userLinks('user123')).toBe('user:user123:links');
expect(CacheKeys.linkStats('link456')).toBe('link:link456:stats');
expect(CacheKeys.userProfile('john')).toBe('profile:john');
expect(CacheKeys.linkRedirect('abc123')).toBe('redirect:abc123');
});
});
describe('Cache Cleanup', () => {
test('should cleanup expired entries', async () => {
const shortTTL = 10; // 10ms
cache.set('key1', 'value1', shortTTL);
cache.set('key2', 'value2', 60000); // 1 minute
// Wait for first key to expire
await new Promise((resolve) => setTimeout(resolve, 20));
cache.cleanup();
expect(cache.get('key1')).toBeNull();
expect(cache.get('key2')).toBe('value2');
});
});
describe('Type Safety', () => {
test('should handle typed values correctly', () => {
interface TestData {
id: string;
name: string;
count: number;
}
const key = 'typed-test';
const value: TestData = { id: '123', name: 'test', count: 42 };
cache.set<TestData>(key, value);
const result = cache.get<TestData>(key);
expect(result).toEqual(value);
expect(result?.id).toBe('123');
expect(result?.count).toBe(42);
});
test('should handle arrays and objects', () => {
const arrayKey = 'array-test';
const arrayValue = [1, 2, 3, 'test'];
const objectKey = 'object-test';
const objectValue = {
nested: { deep: true },
array: [1, 2, 3],
date: new Date().toISOString(),
};
cache.set(arrayKey, arrayValue);
cache.set(objectKey, objectValue);
expect(cache.get(arrayKey)).toEqual(arrayValue);
expect(cache.get(objectKey)).toEqual(objectValue);
});
});
describe('Edge Cases', () => {
test('should handle undefined and null values', () => {
cache.set('null-test', null);
cache.set('undefined-test', undefined);
expect(cache.get('null-test')).toBeNull();
expect(cache.get('undefined-test')).toBeUndefined();
});
test('should handle empty strings and zero values', () => {
cache.set('empty-string', '');
cache.set('zero', 0);
cache.set('false', false);
expect(cache.get('empty-string')).toBe('');
expect(cache.get('zero')).toBe(0);
expect(cache.get('false')).toBe(false);
});
test('should handle concurrent access', () => {
const key = 'concurrent-test';
// Simulate concurrent writes
cache.set(key, 'value1');
cache.set(key, 'value2');
cache.set(key, 'value3');
// Last write should win
expect(cache.get(key)).toBe('value3');
});
test('should handle very long keys', () => {
const longKey = 'a'.repeat(1000);
const value = 'test-value';
cache.set(longKey, value);
expect(cache.get(longKey)).toBe(value);
});
});
describe('Performance', () => {
test('should handle large number of entries efficiently', () => {
const startTime = Date.now();
const entryCount = 1000;
// Set many entries
for (let i = 0; i < entryCount; i++) {
cache.set(`key-${i}`, `value-${i}`);
}
// Get many entries
for (let i = 0; i < entryCount; i++) {
expect(cache.get(`key-${i}`)).toBe(`value-${i}`);
}
const endTime = Date.now();
const duration = endTime - startTime;
// Should complete within reasonable time (1 second for 1000 entries)
expect(duration).toBeLessThan(1000);
});
test('should handle large values efficiently', () => {
const largeValue = {
data: 'x'.repeat(10000),
array: Array(1000).fill('test'),
nested: {
deep: {
very: {
deep: 'value',
},
},
},
};
const key = 'large-value-test';
cache.set(key, largeValue);
const result = cache.get(key);
expect(result).toEqual(largeValue);
});
});
});