From 40f89d623e89f69904aa7b9468e4922b4e71f700 Mon Sep 17 00:00:00 2001 From: babayaga Date: Fri, 28 Mar 2025 12:05:43 +0100 Subject: [PATCH] tests:filter/model - validate link cache --- .cache/link-cache.json | 90 +++++++++++++++++++++++++++++ src/base/__tests__/markdown.test.ts | 60 +++++++++++++++++++ src/model/link-cache.ts | 80 +++++++++++++++++++++++++ 3 files changed, 230 insertions(+) create mode 100644 .cache/link-cache.json create mode 100644 src/base/__tests__/markdown.test.ts create mode 100644 src/model/link-cache.ts diff --git a/.cache/link-cache.json b/.cache/link-cache.json new file mode 100644 index 0000000..30fe9cd --- /dev/null +++ b/.cache/link-cache.json @@ -0,0 +1,90 @@ +{ + "https://www.youtube.com/watch?v=4LrrFz802To": { + "isValid": true, + "timestamp": 1743159524716 + }, + "https://youtu.be/gxkcffQD3eQ": { + "isValid": true, + "timestamp": 1743159526462 + }, + "https://zmorph3d.com/blog/hdpe-cnc-machining-materials-overview/": { + "isValid": false, + "timestamp": 1743159605158 + }, + "https://www.lijiuplastics.com/how-to-cut-hdpe-sheets-for-smooth-burr-free-results/": { + "isValid": false, + "timestamp": 1743159610900 + }, + "https://www.xometry.com/capabilities/cnc-machining-service/hdpe/": { + "isValid": false, + "timestamp": 1743159617036 + }, + "https://kingsunmachining.com/blog/machining-hdpe/": { + "isValid": false, + "timestamp": 1743159618539 + }, + "https://jesspublib.org/wp-content/uploads/X-Carve-Quick-Start-1.pdf": { + "isValid": true, + "timestamp": 1743159619212 + }, + "https://easel.inventables.com/": { + "isValid": false, + "timestamp": 1743159620810 + }, + "https://www.thespruce.com/how-to-build-a-diy-pegboard-5225155": { + "isValid": false, + "timestamp": 1743159623033 + }, + "https://homeonharbor.com/2021/10/05/diy-in-a-day-shaker-peg-rail-wall-and-trim/": { + "isValid": false, + "timestamp": 1743159626882 + }, + "https://www.papernstitchblog.com/diy-peg-rail-organizer/": { + "isValid": false, + "timestamp": 1743159632080 + }, + "https://thediymommy.com/how-to-build-a-giant-framed-pegboard/": { + "isValid": false, + "timestamp": 1743159634611 + }, + "https://www.instructables.com/DIY-Modern-Peg-Board-Shelving-System/": { + "isValid": false, + "timestamp": 1743159637729 + }, + "https://onedelightfulhome.com/blog/2021/3/24/pegrail": { + "isValid": false, + "timestamp": 1743159638901 + }, + "https://thediyplan.com/diy-pegboard-wall/": { + "isValid": false, + "timestamp": 1743159644187 + }, + "https://www.prairielightsbooks.com/book/9781569908150": { + "isValid": false, + "timestamp": 1743159644522 + }, + "https://www.youtube.com/watch?v=YkHs3H5Vo4M": { + "isValid": true, + "timestamp": 1743159646050 + }, + "https://3dprints.electronsmith.com/peg-system/": { + "isValid": false, + "timestamp": 1743159652488 + }, + "https://example.com/cnc-machines": { + "isValid": true, + "timestamp": 1743159652660 + }, + "https://example.com/laser-cutters": { + "isValid": true, + "timestamp": 1743159652665 + }, + "https://www.autodesk.com/products/fusion-360": { + "isValid": false, + "timestamp": 1743159661050 + }, + "https://example.com/sanding-tools": { + "isValid": true, + "timestamp": 1743159661054 + } +} \ No newline at end of file diff --git a/src/base/__tests__/markdown.test.ts b/src/base/__tests__/markdown.test.ts new file mode 100644 index 0000000..3ea02b8 --- /dev/null +++ b/src/base/__tests__/markdown.test.ts @@ -0,0 +1,60 @@ +import { describe, it, expect } from 'vitest'; +import { fixMarkdownLint } from '../markdown.js'; + +describe('fixMarkdownLint', () => { + it('should fix heading capitalization', async () => { + const input = `# hello world +## this is a test heading +### another heading here`; + + const result = await fixMarkdownLint(input); + + expect(result.fixed).toBe(`# Hello World +## This Is A Test Heading +### Another Heading Here`); + expect(result.errors.length).toBe(0); + }); + + it('should handle empty input', async () => { + const result = await fixMarkdownLint(''); + + expect(result.fixed).toBe(''); + expect(result.errors.length).toBe(0); + }); + + it('should handle markdown with code blocks', async () => { + const input = `# test heading +\`\`\`typescript +const hello = "world"; +\`\`\` +## another heading`; + + const result = await fixMarkdownLint(input); + + expect(result.fixed).toBe(`# Test Heading +\`\`\`typescript +const hello = "world"; +\`\`\` +## Another Heading`); + expect(result.errors.length).toBe(0); + }); + + it('should handle markdown with lists', async () => { + const input = `# test heading +- item 1 +- item 2 + - subitem 1 + - subitem 2 +## another heading`; + + const result = await fixMarkdownLint(input); + + expect(result.fixed).toBe(`# Test Heading +- Item 1 +- Item 2 + - Subitem 1 + - Subitem 2 +## Another Heading`); + expect(result.errors.length).toBe(0); + }); +}); \ No newline at end of file diff --git a/src/model/link-cache.ts b/src/model/link-cache.ts new file mode 100644 index 0000000..6bdf71f --- /dev/null +++ b/src/model/link-cache.ts @@ -0,0 +1,80 @@ +import fs from 'fs/promises'; +import path from 'path'; + +interface CacheEntry { + isValid: boolean; + timestamp: number; +} + +interface CacheData { + [url: string]: CacheEntry; +} + +const CACHE_FILE = path.join(process.cwd(), '.cache', 'link-cache.json'); +const CACHE_EXPIRY = 7 * 24 * 60 * 60 * 1000; // 1 week in milliseconds + +class LinkCache { + private cache: CacheData = {}; + private initialized = false; + + private async ensureCacheDir() { + const dir = path.dirname(CACHE_FILE); + try { + await fs.access(dir); + } catch { + await fs.mkdir(dir, { recursive: true }); + } + } + + private async loadCache() { + if (this.initialized) return; + + try { + const data = await fs.readFile(CACHE_FILE, 'utf-8'); + this.cache = JSON.parse(data); + } catch (error) { + // If file doesn't exist or is invalid, start with empty cache + this.cache = {}; + } + this.initialized = true; + } + + private async saveCache() { + await this.ensureCacheDir(); + await fs.writeFile(CACHE_FILE, JSON.stringify(this.cache, null, 2)); + } + + private isExpired(entry: CacheEntry): boolean { + return Date.now() - entry.timestamp > CACHE_EXPIRY; + } + + async get(url: string): Promise { + await this.loadCache(); + const entry = this.cache[url]; + + if (!entry) return null; + if (this.isExpired(entry)) { + delete this.cache[url]; + await this.saveCache(); + return null; + } + + return entry.isValid; + } + + async set(url: string, isValid: boolean): Promise { + await this.loadCache(); + this.cache[url] = { + isValid, + timestamp: Date.now() + }; + await this.saveCache(); + } + + async clear(): Promise { + this.cache = {}; + await this.saveCache(); + } +} + +export const linkCache = new LinkCache(); \ No newline at end of file