import { describe, expect, it } from "@jest/globals"; import { CircularQueue } from "../../src/types/CircularQueue"; interface TestItem { id: number; value: string; } describe("CircularQueue", () => { const testItems = { first: { id: 1, value: "first" }, second: { id: 2, value: "second" }, third: { id: 3, value: "third" }, fourth: { id: 4, value: "fourth" }, test: { id: 1, value: "test" }, apple: { id: 1, value: "apple" }, banana: { id: 2, value: "banana" }, cherry: { id: 3, value: "cherry" }, grape: { id: 5, value: "grape" }, orange: { id: 7, value: "orange" }, a: { id: 1, value: "a" }, b: { id: 2, value: "b" }, c: { id: 3, value: "c" }, d: { id: 4, value: "d" } }; const sortingCallbacks = { byId: (a: TestItem, b: TestItem) => a.id - b.id, byValue: (a: TestItem, b: TestItem) => a.value.localeCompare(b.value) }; const keyExtractors = { id: (item: TestItem) => item.id, value: (item: TestItem) => item.value }; const createQueueWithItems = (size: number, items: TestItem[], sortingCallback: (a: TestItem, b: TestItem) => number) => { const queue = new CircularQueue(size); items.forEach(item => queue.appendWithSorting(item, sortingCallback)); return queue; }; describe("constructor", () => { it("creates queue with specified size", () => { const queue = new CircularQueue(5); expect(queue).toBeDefined(); }); }); describe("appendWithSorting", () => { it("adds items to the queue with sorting callback", () => { const queue = createQueueWithItems(3, [testItems.third, testItems.first, testItems.second], sortingCallbacks.byId); expect(queue.size()).toBe(3); expect(queue.get(0)).toEqual(testItems.first); expect(queue.get(1)).toEqual(testItems.second); expect(queue.get(2)).toEqual(testItems.third); }); it("overwrites oldest items when queue is full", () => { const queue = createQueueWithItems(2, [testItems.first, testItems.second, testItems.third], sortingCallbacks.byId); expect(queue.size()).toBe(2); }); it("handles appending to empty queue", () => { const queue = createQueueWithItems(3, [testItems.test], sortingCallbacks.byId); expect(queue.size()).toBe(1); expect(queue.get(0)).toEqual(testItems.test); }); it("optimizes append when items are already in order", () => { const queue = new CircularQueue(5); let sortCallCount = 0; const trackingSortCallback = (a: TestItem, b: TestItem) => { sortCallCount++; return a.id - b.id; }; queue.appendWithSorting(testItems.first, trackingSortCallback); expect(sortCallCount).toBe(0); queue.appendWithSorting(testItems.second, trackingSortCallback); expect(sortCallCount).toBe(1); queue.appendWithSorting(testItems.third, trackingSortCallback); expect(sortCallCount).toBe(2); queue.appendWithSorting({ id: 0, value: "zero" }, trackingSortCallback); expect(sortCallCount).toBeGreaterThan(3); expect(queue.get(0)).toEqual({ id: 0, value: "zero" }); expect(queue.get(1)).toEqual(testItems.first); }); }); describe("popFront", () => { it("removes the oldest item from queue", () => { const queue = createQueueWithItems(3, [testItems.first, testItems.second], sortingCallbacks.byId); expect(queue.size()).toBe(2); queue.popFront(); expect(queue.size()).toBe(1); expect(queue.get(0)).toEqual(testItems.second); }); it("handles popping from empty queue", () => { const queue = new CircularQueue(3); expect(() => queue.popFront()).not.toThrow(); expect(queue.size()).toBe(0); }); it("handles popping until empty", () => { const queue = createQueueWithItems(2, [testItems.first, testItems.second], sortingCallbacks.byId); queue.popFront(); expect(queue.size()).toBe(1); queue.popFront(); expect(queue.size()).toBe(0); queue.popFront(); expect(queue.size()).toBe(0); }); }); describe("binarySearch", () => { it("finds item using key extractor function", () => { const queue = createQueueWithItems(5, [testItems.apple, testItems.cherry, testItems.grape, testItems.orange], sortingCallbacks.byId); const result = queue.binarySearch(5, keyExtractors.id); expect(result).toEqual(testItems.grape); }); it("returns undefined when item not found", () => { const queue = createQueueWithItems(5, [testItems.apple, testItems.cherry, testItems.orange], sortingCallbacks.byId); const result = queue.binarySearch(5, keyExtractors.id); expect(result).toBeUndefined(); }); it("finds first item", () => { const queue = createQueueWithItems(5, [testItems.apple, testItems.cherry, testItems.orange], sortingCallbacks.byId); const result = queue.binarySearch(1, keyExtractors.id); expect(result).toEqual(testItems.apple); }); it("finds last item", () => { const queue = createQueueWithItems(5, [testItems.apple, testItems.cherry, testItems.orange], sortingCallbacks.byId); const result = queue.binarySearch(7, keyExtractors.id); expect(result).toEqual(testItems.orange); }); it("returns undefined for empty queue", () => { const queue = new CircularQueue(5); const result = queue.binarySearch(1, keyExtractors.id); expect(result).toBeUndefined(); }); it("works with string keys", () => { const queue = createQueueWithItems(5, [testItems.apple, testItems.banana, testItems.cherry], sortingCallbacks.byValue); const result = queue.binarySearch("banana", keyExtractors.value); expect(result).toEqual(testItems.banana); }); it("maintains sorted order assumption", () => { const queue = createQueueWithItems(5, [testItems.d, testItems.a, testItems.c, testItems.b], sortingCallbacks.byValue); expect(queue.binarySearch("a", keyExtractors.value)).toEqual(testItems.a); expect(queue.binarySearch("b", keyExtractors.value)).toEqual(testItems.b); expect(queue.binarySearch("c", keyExtractors.value)).toEqual(testItems.c); expect(queue.binarySearch("d", keyExtractors.value)).toEqual(testItems.d); expect(queue.binarySearch("z", keyExtractors.value)).toBeUndefined(); }); }); describe("integration", () => { it("handles appendWithSorting, popFront, and binarySearch together", () => { const queue = createQueueWithItems(3, [testItems.third, testItems.first, testItems.second], sortingCallbacks.byId); expect(queue.binarySearch(2, keyExtractors.id)).toEqual(testItems.second); queue.popFront(); expect(queue.binarySearch(1, keyExtractors.id)).toBeUndefined(); expect(queue.binarySearch(2, keyExtractors.id)).toEqual(testItems.second); queue.appendWithSorting(testItems.fourth, sortingCallbacks.byId); expect(queue.binarySearch(4, keyExtractors.id)).toEqual(testItems.fourth); }); }); });