Files
welshman/packages/lib/__tests__/LRUCache.test.ts
T
2025-02-25 14:34:49 -08:00

105 lines
3.1 KiB
TypeScript

import {describe, it, expect, beforeEach, vi} from "vitest"
import {LRUCache, cached, simpleCache} from "../src/LRUCache"
describe("Caches", () => {
describe("LRUCache", () => {
describe("basic operations", () => {
let cache: LRUCache<string, number>
beforeEach(() => {
cache = new LRUCache(3) // Max size of 3
})
it("should set and get values", () => {
cache.set("a", 1)
expect(cache.get("a")).toBe(1)
})
it("should check if key exists", () => {
cache.set("a", 1)
expect(cache.has("a")).toBe(true)
expect(cache.has("b")).toBe(false)
})
it("should evict least recently used items when exceeding maxSize", () => {
cache.set("a", 1)
cache.set("b", 2)
cache.set("c", 3)
cache.set("d", 4)
expect(cache.has("a")).toBe(false) // 'a' should be evicted
expect(cache.get("b")).toBe(2)
expect(cache.get("c")).toBe(3)
expect(cache.get("d")).toBe(4)
})
it("should update access order on get", () => {
cache.set("a", 1) // keys = [a]
cache.set("b", 2) // keys = [a, b]
cache.set("c", 3) // keys = [a, b, c]
cache.get("b") // keys = [a, b, c, b]
cache.get("b") // keys = [a, b, c, b, b]
cache.get("b") // keys = [a, b, c, b, b, b] size at limit (maxSize * 2 = 6)
cache.get("a") // keys = [b, b, a] keys is over limit, only the 3 last are kept
cache.set("d", 4) // keys = [b, b, a, d],
// @todo clarify with @staab the intended behavior
// "a" was recently accessed, it should not be evicted
expect(cache.has("a")).toBe(true) // 'a' should be present
expect(cache.has("b")).toBe(false) // 'b' should be evicted
})
})
})
describe("cached function", () => {
it("should cache function results", () => {
const mockGetValue = vi.fn((args: [number]) => args[0] * 2)
const cachedFn = cached({
maxSize: 2,
getKey: (args: [number]) => args[0],
getValue: mockGetValue,
})
expect(cachedFn(1)).toBe(2)
expect(cachedFn(1)).toBe(2)
expect(mockGetValue).toHaveBeenCalledTimes(1) // Should only compute once
})
it("should respect maxSize", () => {
const cachedFn = cached({
maxSize: 2,
getKey: (args: [number]) => args[0],
getValue: (args: [number]) => args[0] * 2,
})
cachedFn(1)
cachedFn(2)
cachedFn(3)
expect(cachedFn.cache.has(1)).toBe(false) // Should be evicted
expect(cachedFn.cache.has(2)).toBe(true)
expect(cachedFn.cache.has(3)).toBe(true)
})
})
describe("simpleCache", () => {
it("should cache function results with default settings", () => {
const mockFn = vi.fn((v: number[]) => v[0] + v[1])
const cachedFn = simpleCache(mockFn)
expect(cachedFn(1, 2)).toBe(3)
expect(cachedFn(1, 2)).toBe(3)
expect(mockFn).toHaveBeenCalledTimes(1)
})
it("should use string join as default key", () => {
const cachedFn = simpleCache((v: number[]) => v[0] + v[1])
cachedFn(1, 2)
expect(cachedFn.cache.has("1:2")).toBe(true)
})
})
})