feat: add comprehensive tests for WorldBlockCache functionality

This commit is contained in:
MayaTheShy
2026-03-22 11:47:57 -04:00
parent 56fc79f5f2
commit aa3b166453

View File

@@ -0,0 +1,127 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { WorldBlockCache } from '../WorldBlockCache.js';
function makeDb() {
const blocks = new Map();
return {
getBlock: vi.fn((x, y, z) => {
const key = `${x},${y},${z}`;
return blocks.get(key) || null;
}),
getWorldBlocks: vi.fn((limit) => {
const all = [];
for (const [key, val] of blocks) {
const [x, y, z] = key.split(',').map(Number);
all.push({ x, y, z, block_name: val.name, metadata: val.metadata || 0, discovered_by: val.discoveredBy, discovered_at: val.timestamp });
if (all.length >= limit) break;
}
return all;
}),
getWorldBlockCount: vi.fn(() => blocks.size),
getWorldBlocksInArea: vi.fn(() => []),
// Helper for test setup
_blocks: blocks,
_addBlock(x, y, z, name) {
blocks.set(`${x},${y},${z}`, { name, metadata: 0, discoveredBy: 1, timestamp: Date.now() });
}
};
}
describe('WorldBlockCache', () => {
let db, cache;
beforeEach(() => {
db = makeDb();
cache = new WorldBlockCache(db, 5); // Small capacity for testing eviction
});
it('should return undefined for missing blocks', () => {
expect(cache.get('0,0,0')).toBeUndefined();
expect(db.getBlock).toHaveBeenCalledWith(0, 0, 0);
});
it('should cache blocks from DB on first access', () => {
db._addBlock(1, 2, 3, 'minecraft:stone');
const block = cache.get('1,2,3');
expect(block).toBeDefined();
expect(block.name).toBe('minecraft:stone');
// Second access should not hit DB
cache.get('1,2,3');
expect(db.getBlock).toHaveBeenCalledTimes(1);
});
it('should set and retrieve blocks', () => {
cache.set('5,5,5', { name: 'minecraft:dirt', metadata: 0 });
const block = cache.get('5,5,5');
expect(block.name).toBe('minecraft:dirt');
// Should not hit DB since we just set it
expect(db.getBlock).not.toHaveBeenCalled();
});
it('should delete blocks from cache', () => {
cache.set('1,1,1', { name: 'minecraft:stone' });
expect(cache.delete('1,1,1')).toBe(true);
// Now should fall through to DB
const result = cache.get('1,1,1');
expect(db.getBlock).toHaveBeenCalledWith(1, 1, 1);
});
it('should evict LRU entries when over capacity', () => {
// Fill cache to capacity (5)
for (let i = 0; i < 5; i++) {
cache.set(`${i},0,0`, { name: `block_${i}` });
}
// Access block_0 to make it recently used
cache.get('0,0,0');
// Add one more → should evict the LRU entry (block_1, since block_0 was just accessed)
cache.set('5,0,0', { name: 'block_5' });
// block_1 should have been evicted (we check internal cache size)
expect(cache._cache.size).toBe(5);
expect(cache._cache.has('1,0,0')).toBe(false);
expect(cache._cache.has('0,0,0')).toBe(true); // recently used
});
it('should report size from DB count', () => {
db._addBlock(1, 1, 1, 'stone');
db._addBlock(2, 2, 2, 'dirt');
expect(cache.size).toBe(2);
expect(db.getWorldBlockCount).toHaveBeenCalledTimes(1);
// Cached — no second call
const _ = cache.size;
expect(db.getWorldBlockCount).toHaveBeenCalledTimes(1);
});
it('should invalidate size cache on set/delete', () => {
const _ = cache.size;
expect(db.getWorldBlockCount).toHaveBeenCalledTimes(1);
cache.set('1,1,1', { name: 'stone' });
const __ = cache.size;
expect(db.getWorldBlockCount).toHaveBeenCalledTimes(2);
});
it('should iterate all entries from DB', () => {
db._addBlock(1, 1, 1, 'stone');
db._addBlock(2, 2, 2, 'dirt');
const entries = [...cache.entries()];
expect(entries).toHaveLength(2);
expect(db.getWorldBlocks).toHaveBeenCalled();
});
it('should return blocks formatted for API', () => {
db._addBlock(3, 3, 3, 'minecraft:diamond_ore');
const blocks = cache.getAllBlocksForAPI(100);
expect(blocks).toHaveLength(1);
expect(blocks[0]).toMatchObject({ x: 3, y: 3, z: 3, name: 'minecraft:diamond_ore' });
});
});