diff --git a/scripts/build-dist.mjs b/scripts/build-dist.mjs index b30f1167..02a633f0 100644 --- a/scripts/build-dist.mjs +++ b/scripts/build-dist.mjs @@ -38,8 +38,20 @@ function copyFile(src, dest) { if (fs.existsSync(src)) { fs.copyFileSync(src, dest); } else { - console.error(`Error: Source file ${src} does not exist.`); - process.exit(1); + const message = `Error: Source file ${src} does not exist.`; + console.error(message); + // Debug: list source directory to check what IS there + try { + console.log(`Source directory listing for context:`); + const dir = path.dirname(src); + console.log(fs.readdirSync(dir || '.')); + } catch (e) { + console.error('Failed to list source directory:', e); + } + + if (['_headers', '_redirects', 'index.html'].includes(path.basename(src))) { + throw new Error(message); + } } } @@ -75,6 +87,25 @@ function main() { copyFile(file, path.join(DIST, file)); } + // Verify critical CSS generation + const generatedCssSource = path.join('css', 'tailwind.generated.css'); + const generatedCssDist = path.join(DIST, 'css', 'tailwind.generated.css'); + + if (!fs.existsSync(generatedCssSource) && !fs.existsSync(generatedCssDist)) { + console.error('Error: css/tailwind.generated.css was not generated.'); + try { + console.log('Listing css/ directory:'); + if (fs.existsSync('css')) { + console.log(fs.readdirSync('css')); + } else { + console.log('css/ directory does not exist.'); + } + } catch (e) { + console.error('Failed to list css directory:', e); + } + process.exit(1); + } + console.log('Copying directories...'); for (const dir of DIRS_TO_COPY) { copyDir(dir, path.join(DIST, dir)); diff --git a/tests/utils/lruCache.test.mjs b/tests/utils/lruCache.test.mjs new file mode 100644 index 00000000..199c50fc --- /dev/null +++ b/tests/utils/lruCache.test.mjs @@ -0,0 +1,86 @@ +import { describe, it, beforeEach } from "node:test"; +import assert from "node:assert/strict"; +import { LRUCache } from "../../js/utils/lruCache.js"; + +describe("LRUCache", () => { + let cache; + + beforeEach(() => { + cache = new LRUCache({ maxSize: 3 }); + }); + + it("should initialize with default options", () => { + const defaultCache = new LRUCache(); + assert.equal(defaultCache.maxSize, 100); + assert.equal(defaultCache.size(), 0); + }); + + it("should store and retrieve values", () => { + cache.set("a", 1); + assert.equal(cache.get("a"), 1); + assert.equal(cache.has("a"), true); + assert.equal(cache.size(), 1); + }); + + it("should evict oldest item when limit is reached", () => { + cache.set("a", 1); + cache.set("b", 2); + cache.set("c", 3); + assert.equal(cache.size(), 3); + + cache.set("d", 4); // Should evict 'a' + assert.equal(cache.size(), 3); + assert.equal(cache.has("a"), false); + assert.equal(cache.get("b"), 2); + assert.equal(cache.get("d"), 4); + }); + + it("should refresh recency on access", () => { + cache.set("a", 1); + cache.set("b", 2); + cache.set("c", 3); + + // Access 'a', making it newest + cache.get("a"); + + cache.set("d", 4); // Should evict 'b' (oldest is now 'b') + + assert.equal(cache.has("b"), false); + assert.equal(cache.has("a"), true); + assert.equal(cache.has("c"), true); + assert.equal(cache.has("d"), true); + }); + + it("should update value and refresh recency on set", () => { + cache.set("a", 1); + cache.set("b", 2); + cache.set("c", 3); + + // Update 'a' + cache.set("a", 10); + + cache.set("d", 4); // Should evict 'b' + + assert.equal(cache.get("a"), 10); + assert.equal(cache.has("b"), false); + }); + + it("should track stats", () => { + cache.set("a", 1); + cache.get("a"); // Hit + cache.get("b"); // Miss + + const stats = cache.getStats(); + assert.equal(stats.hits, 1); + assert.equal(stats.misses, 1); + assert.equal(stats.size, 1); + assert.equal(stats.maxSize, 3); + }); + + it("should clear cache", () => { + cache.set("a", 1); + cache.clear(); + assert.equal(cache.size(), 0); + assert.equal(cache.getStats().hits, 0); + }); +});