@speckle/objectloader2
Version:
This is an updated objectloader for the Speckle viewer written in typescript
151 lines • 6.28 kB
JavaScript
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import { MemoryCache, MemoryCacheItem } from './MemoryCache.js';
const logger = () => {
// console.log(message, ...optionalParams)
};
const defaultOptions = {
maxSizeInMb: 10,
ttlms: 1000
};
const createItem = (id, size, referencedIds = []) => {
const base = {
id,
// eslint-disable-next-line camelcase
speckle_type: 'Base'
};
if (referencedIds.length > 0) {
base.data = referencedIds.map((referencedId) => ({
// eslint-disable-next-line camelcase
speckle_type: 'reference',
referencedId
}));
}
return {
baseId: id,
base,
size
};
};
describe('MemoryCacheItem', () => {
it('should correctly determine if it is expired', () => {
const item = createItem('1', 100);
const now = Date.now();
const cacheItem = new MemoryCacheItem(item, now + 1000);
expect(cacheItem.isExpired(now + 500)).toBe(false);
expect(cacheItem.isExpired(now + 1500)).toBe(true);
});
it('should update its access time', () => {
const item = createItem('1', 100);
const now = Date.now();
const cacheItem = new MemoryCacheItem(item, now + 1000);
cacheItem.setAccess(now + 500, 1000);
expect(cacheItem.isExpired(now + 1000)).toBe(false);
expect(cacheItem.isExpired(now + 1600)).toBe(true);
});
it('should return the correct item', () => {
const item = createItem('1', 100);
const cacheItem = new MemoryCacheItem(item, Date.now() + 1000);
expect(cacheItem.getItem()).toEqual(item);
});
it('should be done if expired', () => {
const item = createItem('1', 100);
const now = Date.now();
const cacheItem = new MemoryCacheItem(item, now + 1000);
expect(cacheItem.done(now + 1500)).toBe(true);
expect(cacheItem.done(now + 500)).toBe(false);
});
});
describe('MemoryCache', () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.useRealTimers();
});
it('should add and get an item', () => {
const cache = new MemoryCache(defaultOptions, logger);
const item = createItem('1', 100);
const requestItem = vi.fn();
cache.add(item, requestItem);
const retrieved = cache.get('1');
expect(retrieved).toEqual(item);
expect(requestItem).not.toHaveBeenCalled();
});
it('should update expiry on get', () => {
const cache = new MemoryCache({ ...defaultOptions, ttlms: 100 }, logger);
const item = createItem('1', 100);
cache.add(item, vi.fn());
vi.advanceTimersByTime(50);
const retrieved = cache.get('1');
expect(retrieved).toBeDefined();
vi.advanceTimersByTime(80);
const retrievedAgain = cache.get('1');
expect(retrievedAgain).toBeDefined();
});
it('should return undefined for non-existent item', () => {
const cache = new MemoryCache(defaultOptions, logger);
expect(cache.get('non-existent')).toBeUndefined();
});
it('should scan for references and request missing ones', () => {
const cache = new MemoryCache(defaultOptions, logger);
const requestItem = vi.fn();
const item = createItem('1', 100, ['2', '3']);
cache.add(createItem('3', 50), vi.fn()); // pre-cache one of the references
cache.scanForReferences(item.base, requestItem);
expect(requestItem).toHaveBeenCalledTimes(1);
expect(requestItem).toHaveBeenCalledWith('2');
expect(requestItem).not.toHaveBeenCalledWith('3');
});
it('should clean up expired items when size exceeds max', () => {
const options = { maxSizeInMb: 0, ttlms: 100 }; // 100 bytes
const cache = new MemoryCache(options, logger);
const requestItem = vi.fn();
const item1 = createItem('1', 60);
const item2 = createItem('2', 60);
const now = 1;
cache.add(item1, requestItem, now - 200);
cache.cleanCache(now);
cache.add(item2, requestItem, now + 100);
expect(cache.get('1')).toBeUndefined();
expect(cache.get('2')).toBeDefined();
});
it('should not clean up expired items if they have references', () => {
const options = { maxSizeInMb: 0.0001, ttlms: 100 }; // 100 bytes
const cache = new MemoryCache(options, logger);
const requestItem = vi.fn();
const item1 = createItem('1', 60);
const item2 = createItem('2', 60, ['1']);
cache.add(item1, requestItem);
vi.advanceTimersByTime(110); // item1 expires
cache.add(item2, requestItem); // item2 adds a reference to item1
vi.advanceTimersByTime(100);
expect(cache.get('1')).toBeDefined();
expect(cache.get('2')).toBeDefined();
});
it('compareMaybeBasesByReferences should sort correctly', () => {
const cache = new MemoryCache(defaultOptions, logger);
const requestItem = vi.fn();
const item1 = createItem('1', 10);
const item2 = createItem('2', 10);
const item3 = createItem('3', 10, ['1', '2']);
const item4 = createItem('4', 10, ['2']);
cache.add(item1, requestItem);
cache.add(item2, requestItem);
cache.add(item3, requestItem);
cache.add(item4, requestItem);
expect(cache.compareMaybeBasesByReferences('1', '2')).toBe(-1);
expect(cache.compareMaybeBasesByReferences('2', '1')).toBe(1);
expect(cache.compareMaybeBasesByReferences('1', '1')).toBe(0);
expect(cache.compareMaybeBasesByReferences('1', '5')).toBe(1);
expect(cache.compareMaybeBasesByReferences('5', '1')).toBe(-1);
expect(cache.compareMaybeBasesByReferences('5', '6')).toBe(0);
});
it('should throw when used after dispose', () => {
const cache = new MemoryCache(defaultOptions, logger);
cache.dispose();
const item = createItem('1', 100);
expect(() => cache.add(item, vi.fn())).toThrow('MemoryCache is disposed');
expect(() => cache.get('1')).toThrow('MemoryCache is disposed');
});
});
//# sourceMappingURL=MemoryCache.test.js.map