Just a whole lot of crap

This commit is contained in:
2025-08-14 16:36:12 -05:00
parent 66b7e42275
commit 62f6d6f279
144 changed files with 41421 additions and 2458 deletions

View File

@@ -0,0 +1,526 @@
const React = require("react");
const { render } = require("ink-testing-library");
const {
useEventListener,
useInterval,
useTimeout,
useAsyncOperation,
useMemoryMonitor,
useWeakRef,
useCleanup,
useResourcePool,
} = require("../../../src/tui/hooks/useMemoryManagement.js");
const {
withMemoryManagement,
MemoryOptimizedContainer,
MemoryEfficientList,
AutoCleanupComponent,
} = require("../../../src/tui/components/common/MemoryOptimizedComponent.jsx");
/**
* Memory management tests for TUI components
* Requirements: 4.2, 4.5
*/
describe("Memory Management Hooks", () => {
describe("useCleanup", () => {
test("should execute cleanup functions on unmount", () => {
const cleanupFn1 = jest.fn();
const cleanupFn2 = jest.fn();
const TestComponent = () => {
const { addCleanup } = useCleanup();
React.useEffect(() => {
addCleanup(cleanupFn1);
addCleanup(cleanupFn2);
}, [addCleanup]);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
expect(cleanupFn1).not.toHaveBeenCalled();
expect(cleanupFn2).not.toHaveBeenCalled();
unmount();
expect(cleanupFn1).toHaveBeenCalledTimes(1);
expect(cleanupFn2).toHaveBeenCalledTimes(1);
});
test("should handle cleanup function errors gracefully", () => {
const consoleSpy = jest
.spyOn(console, "error")
.mockImplementation(() => {});
const goodCleanup = jest.fn();
const badCleanup = jest.fn(() => {
throw new Error("Cleanup error");
});
const TestComponent = () => {
const { addCleanup } = useCleanup();
React.useEffect(() => {
addCleanup(badCleanup);
addCleanup(goodCleanup);
}, [addCleanup]);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
unmount();
expect(badCleanup).toHaveBeenCalled();
expect(goodCleanup).toHaveBeenCalled();
expect(consoleSpy).toHaveBeenCalledWith(
"Error during cleanup:",
expect.any(Error)
);
consoleSpy.mockRestore();
});
});
describe("useAsyncOperation", () => {
test("should cancel operations on unmount", async () => {
let operationCancelled = false;
const asyncOperation = () =>
new Promise((resolve, reject) => {
setTimeout(() => {
if (operationCancelled) {
reject(new Error("Operation cancelled"));
} else {
resolve("success");
}
}, 100);
});
const TestComponent = () => {
const { executeAsync, cancelAllOperations } = useAsyncOperation();
React.useEffect(() => {
executeAsync(asyncOperation).catch((error) => {
if (error.message === "Operation cancelled") {
operationCancelled = true;
}
});
}, [executeAsync]);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
// Unmount before operation completes
setTimeout(() => unmount(), 50);
// Wait for operation to complete or be cancelled
await new Promise((resolve) => setTimeout(resolve, 150));
expect(operationCancelled).toBe(true);
});
test("should not execute callbacks after unmount", async () => {
const onSuccess = jest.fn();
const onError = jest.fn();
const TestComponent = () => {
const { executeAsync } = useAsyncOperation();
React.useEffect(() => {
const asyncOp = () => Promise.resolve("success");
executeAsync(asyncOp, onSuccess, onError);
}, [executeAsync]);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
// Unmount immediately
unmount();
// Wait for async operation
await new Promise((resolve) => setTimeout(resolve, 50));
expect(onSuccess).not.toHaveBeenCalled();
expect(onError).not.toHaveBeenCalled();
});
});
describe("useInterval", () => {
test("should clear interval on unmount", () => {
jest.useFakeTimers();
const callback = jest.fn();
const TestComponent = () => {
useInterval(callback, 1000);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
// Fast-forward time
jest.advanceTimersByTime(2000);
expect(callback).toHaveBeenCalledTimes(2);
unmount();
// Fast-forward more time after unmount
jest.advanceTimersByTime(2000);
expect(callback).toHaveBeenCalledTimes(2); // Should not increase
jest.useRealTimers();
});
test("should provide manual control over interval", () => {
jest.useFakeTimers();
const callback = jest.fn();
const TestComponent = () => {
const { start, stop, restart } = useInterval(callback, 1000);
React.useEffect(() => {
// Test manual control
setTimeout(() => stop(), 1500);
setTimeout(() => start(), 2500);
setTimeout(() => restart(), 3500);
}, [start, stop, restart]);
return React.createElement("div", null, "Test");
};
render(React.createElement(TestComponent));
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(1000); // 2000ms total, stopped at 1500ms
expect(callback).toHaveBeenCalledTimes(1);
jest.advanceTimersByTime(1000); // 3000ms total, restarted at 2500ms
expect(callback).toHaveBeenCalledTimes(2);
jest.useRealTimers();
});
});
describe("useTimeout", () => {
test("should clear timeout on unmount", () => {
jest.useFakeTimers();
const callback = jest.fn();
const TestComponent = () => {
useTimeout(callback, 1000);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
// Unmount before timeout
unmount();
// Fast-forward past timeout
jest.advanceTimersByTime(1500);
expect(callback).not.toHaveBeenCalled();
jest.useRealTimers();
});
});
describe("useMemoryMonitor", () => {
test("should track render count", () => {
let renderCount = 0;
const TestComponent = ({ value }) => {
const { renderCount: currentRenderCount } =
useMemoryMonitor("TestComponent");
renderCount = currentRenderCount;
return React.createElement("div", null, value);
};
const { rerender } = render(
React.createElement(TestComponent, { value: 1 })
);
expect(renderCount).toBe(1);
rerender(React.createElement(TestComponent, { value: 2 }));
expect(renderCount).toBe(2);
rerender(React.createElement(TestComponent, { value: 3 }));
expect(renderCount).toBe(3);
});
test("should provide memory statistics", () => {
let memoryStats = null;
const TestComponent = () => {
const { getMemoryStats } = useMemoryMonitor("TestComponent");
React.useEffect(() => {
// Simulate some memory usage
setTimeout(() => {
memoryStats = getMemoryStats();
}, 100);
}, [getMemoryStats]);
return React.createElement("div", null, "Test");
};
render(React.createElement(TestComponent));
return new Promise((resolve) => {
setTimeout(() => {
// Memory stats might be null in test environment
// but the function should exist
expect(typeof memoryStats).toBeDefined();
resolve();
}, 150);
});
});
});
describe("useWeakRef", () => {
test("should store and retrieve values using weak references", () => {
let getValue, setValue;
const TestComponent = () => {
[getValue, setValue] = useWeakRef();
return React.createElement("div", null, "Test");
};
render(React.createElement(TestComponent));
const testObject = { data: "test" };
setValue(testObject);
expect(getValue()).toBe(testObject);
setValue(null);
expect(getValue()).toBe(null);
});
});
describe("useResourcePool", () => {
test("should manage resource pool efficiently", () => {
let resourcePool;
const createResource = jest.fn(() => ({ id: Math.random() }));
const resetResource = jest.fn();
const TestComponent = () => {
resourcePool = useResourcePool(createResource, resetResource, 3);
return React.createElement("div", null, "Test");
};
render(React.createElement(TestComponent));
// Acquire resources
const resource1 = resourcePool.acquire();
const resource2 = resourcePool.acquire();
expect(createResource).toHaveBeenCalledTimes(2);
expect(resourcePool.activeCount).toBe(2);
expect(resourcePool.poolSize).toBe(0);
// Release resources
resourcePool.release(resource1);
expect(resourcePool.activeCount).toBe(1);
expect(resourcePool.poolSize).toBe(1);
// Acquire again (should reuse)
const resource3 = resourcePool.acquire();
expect(createResource).toHaveBeenCalledTimes(2); // No new creation
expect(resetResource).toHaveBeenCalledTimes(1);
});
});
});
describe("Memory Optimized Components", () => {
describe("withMemoryManagement HOC", () => {
test("should provide memory management props to wrapped component", () => {
let receivedProps = {};
const TestComponent = (props) => {
receivedProps = props;
return React.createElement("div", null, "Test");
};
const MemoryManagedComponent = withMemoryManagement(TestComponent, {
componentName: "TestComponent",
});
render(
React.createElement(MemoryManagedComponent, { testProp: "value" })
);
expect(receivedProps.testProp).toBe("value");
expect(typeof receivedProps.addCleanup).toBe("function");
expect(typeof receivedProps.executeAsync).toBe("function");
expect(typeof receivedProps.getMemoryStats).toBe("function");
expect(typeof receivedProps.renderCount).toBe("number");
});
});
describe("MemoryOptimizedContainer", () => {
test("should display memory warnings when threshold is exceeded", () => {
// Mock process.memoryUsage to return high memory usage
const originalMemoryUsage = process.memoryUsage;
process.memoryUsage = jest.fn(() => ({
heapUsed: 200 * 1024 * 1024, // 200MB
heapTotal: 250 * 1024 * 1024,
external: 10 * 1024 * 1024,
rss: 300 * 1024 * 1024,
}));
const onMemoryWarning = jest.fn();
const { lastFrame } = render(
React.createElement(
MemoryOptimizedContainer,
{
memoryThreshold: 100 * 1024 * 1024, // 100MB threshold
memoryCheckInterval: 100, // Fast check for testing
onMemoryWarning,
},
"Test content"
)
);
// Wait for memory check
return new Promise((resolve) => {
setTimeout(() => {
const output = lastFrame();
expect(output).toContain("Memory Warning");
process.memoryUsage = originalMemoryUsage;
resolve();
}, 150);
});
});
});
describe("MemoryEfficientList", () => {
test("should limit cached items to prevent memory bloat", () => {
const items = Array.from({ length: 200 }, (_, i) => `Item ${i}`);
const renderItem = (item, index) =>
React.createElement("div", { key: index }, item);
const { lastFrame } = render(
React.createElement(MemoryEfficientList, {
items,
renderItem,
maxCachedItems: 50,
})
);
const output = lastFrame();
expect(output).toContain("Cached:");
expect(output).toContain("50"); // Should show cache limit
});
});
describe("AutoCleanupComponent", () => {
test("should cleanup old resources automatically", () => {
jest.useFakeTimers();
let resourceUtils = {};
const TestComponent = (utils) => {
resourceUtils = utils;
return React.createElement("div", null, "Test");
};
render(
React.createElement(
AutoCleanupComponent,
{
cleanupInterval: 1000,
maxAge: 2000,
},
TestComponent
)
);
// Add a resource
const mockResource = {
cleanup: jest.fn(),
};
resourceUtils.addResource("test", mockResource);
expect(resourceUtils.resourceCount).toBe(1);
// Fast-forward past maxAge
jest.advanceTimersByTime(3000);
expect(mockResource.cleanup).toHaveBeenCalled();
expect(resourceUtils.resourceCount).toBe(0);
jest.useRealTimers();
});
});
});
describe("Memory Leak Detection", () => {
test("should detect potential memory leaks in component lifecycle", async () => {
const components = [];
// Create multiple components with potential leaks
for (let i = 0; i < 10; i++) {
const TestComponent = () => {
const [data, setData] = React.useState([]);
React.useEffect(() => {
// Simulate memory leak by accumulating data
const interval = setInterval(() => {
setData((prev) => [...prev, new Array(1000).fill(i)]);
}, 10);
return () => clearInterval(interval);
}, []);
return React.createElement("div", null, `Component ${i}`);
};
const { unmount } = render(React.createElement(TestComponent));
components.push(unmount);
}
// Let components run for a bit
await new Promise((resolve) => setTimeout(resolve, 100));
// Unmount all components
components.forEach((unmount) => unmount());
// Wait for cleanup
await new Promise((resolve) => setTimeout(resolve, 100));
// In a real scenario, we would check memory usage here
// For testing, we just verify that components were created and destroyed
expect(components).toHaveLength(10);
});
test("should properly cleanup event listeners", () => {
const mockAddEventListener = jest.fn();
const mockRemoveEventListener = jest.fn();
// Mock DOM element
const mockElement = {
addEventListener: mockAddEventListener,
removeEventListener: mockRemoveEventListener,
};
const TestComponent = () => {
useEventListener("click", () => {}, mockElement);
return React.createElement("div", null, "Test");
};
const { unmount } = render(React.createElement(TestComponent));
expect(mockAddEventListener).toHaveBeenCalledTimes(1);
expect(mockRemoveEventListener).not.toHaveBeenCalled();
unmount();
expect(mockRemoveEventListener).toHaveBeenCalledTimes(1);
});
});