Skip to main content
Glama
component-integration.test.ts12.2 kB
/** * Integration Tests - Component Interactions * Tests how different components work together */ import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { WritingStorage } from "../../storage/WritingStorage.js"; import { SQLiteManager } from "../../storage/SQLiteManager.js"; import { MarkdownParser } from "../../markdown/MarkdownParser.js"; import { MarkdownChunker } from "../../markdown/MarkdownChunker.js"; import { LinkAnalyzer } from "../../markdown/LinkAnalyzer.js"; import { TerminologyChecker } from "../../analysis/TerminologyChecker.js"; import { StructureValidator } from "../../analysis/StructureValidator.js"; import fs from "fs"; import os from "os"; import path from "path"; describe("Integration: Component Interactions", () => { let storage: WritingStorage; let parser: MarkdownParser; let chunker: MarkdownChunker; let linkAnalyzer: LinkAnalyzer; const testDbPath = path.join(os.tmpdir(), "test-integration.db"); beforeEach(() => { if (fs.existsSync(testDbPath)) { fs.unlinkSync(testDbPath); } const sqliteManager = new SQLiteManager({ dbPath: testDbPath }); storage = new WritingStorage(sqliteManager); parser = new MarkdownParser(); chunker = new MarkdownChunker(); linkAnalyzer = new LinkAnalyzer(storage); }); afterEach(() => { storage.close(); if (fs.existsSync(testDbPath)) { fs.unlinkSync(testDbPath); } }); describe("Parser → Storage Integration", () => { it("should parse and store markdown file", async () => { const content = `--- title: Test Document author: Test Author --- # Main Title This is a test paragraph. ## Subsection More content here.`; const parsed = parser.parse("test.md", content); await storage.addFile({ filePath: "test.md", content, title: parsed.metadata.title || "Test Document", }); const files = storage.getFiles(); expect(files.length).toBe(1); expect(files[0].title).toBe("Test Document"); }); it("should parse headings and store them", async () => { const content = "# H1\n## H2\n### H3\n## Another H2"; const parsed = parser.parse("test.md", content); await storage.addFile({ filePath: "test.md", content, title: "Test", }); const headings = storage.getHeadings("test.md"); expect(headings.length).toBeGreaterThan(0); }); }); describe("Parser → Chunker Integration", () => { it("should parse then chunk content", () => { const content = `# Chapter 1 Introduction paragraph. ## Section 1.1 Section content. ## Section 1.2 More section content.`; const parsed = parser.parse("test.md", content); const chunks = chunker.chunk("test.md", content, parsed.headings); expect(chunks.length).toBeGreaterThan(0); chunks.forEach((chunk) => { expect(chunk).toHaveProperty("heading"); expect(chunk).toHaveProperty("content"); }); }); it("should maintain heading context in chunks", () => { const content = `# Main Content under main. ## Sub1 Content under sub1. ## Sub2 Content under sub2.`; const parsed = parser.parse("test.md", content); const chunks = chunker.chunk("test.md", content, parsed.headings); expect(chunks.some((c) => c.heading.includes("Main"))).toBe(true); expect(chunks.some((c) => c.heading.includes("Sub1"))).toBe(true); expect(chunks.some((c) => c.heading.includes("Sub2"))).toBe(true); }); }); describe("LinkAnalyzer → Storage Integration", () => { it("should extract and store links", async () => { const content = `# Document [Link 1](other.md) [Link 2](https://example.com) [[Wiki Link]]`; await storage.addFile({ filePath: "test.md", content, title: "Test", }); const fileId = storage.getFiles()[0].id; await linkAnalyzer.extractLinks(fileId, "test.md", content); const links = storage.getLinks("test.md"); expect(links.length).toBeGreaterThan(0); }); it("should categorize link types correctly", async () => { const content = `[Internal](doc.md) [External](https://example.com) [[WikiLink]] [Anchor](#section)`; await storage.addFile({ filePath: "test.md", content, title: "Test", }); const fileId = storage.getFiles()[0].id; await linkAnalyzer.extractLinks(fileId, "test.md", content); const links = storage.getLinks("test.md"); const hasMarkdown = links.some((l) => l.linkType === "markdown"); const hasExternal = links.some((l) => l.linkType === "external"); const hasWiki = links.some((l) => l.linkType === "wiki"); expect(hasMarkdown || hasExternal || hasWiki).toBe(true); }); }); describe("Storage → TerminologyChecker Integration", () => { it("should check terminology from stored content", async () => { await storage.addFile({ filePath: "file1.md", content: "Use email for contact. Send email to support.", title: "File 1", }); await storage.addFile({ filePath: "file2.md", content: "Use e-mail for contact. Send e-mail to support.", title: "File 2", }); const checker = new TerminologyChecker(storage); const report = await checker.checkTerminology({ terms: ["email"] }); if (report.groups.length > 0) { expect(report.groups[0].variants.length).toBeGreaterThan(1); } }); it("should find variants across multiple files", async () => { await storage.addFile({ filePath: "file1.md", content: "The API is RESTful.", title: "File 1", }); await storage.addFile({ filePath: "file2.md", content: "Call the api endpoint.", title: "File 2", }); await storage.addFile({ filePath: "file3.md", content: "The Api responds quickly.", title: "File 3", }); const checker = new TerminologyChecker(storage); const report = await checker.checkTerminology({ terms: ["api"] }); if (report.groups.length > 0) { const apiGroup = report.groups[0]; expect(apiGroup.variants.length).toBeGreaterThan(1); } }); }); describe("Storage → StructureValidator Integration", () => { it("should validate structure from stored content", async () => { const content = "# Title\n### Skipped Level\nContent"; await storage.addFile({ filePath: "test.md", content, title: "Test", }); const validator = new StructureValidator(storage); const report = await validator.validateStructure({ filePath: "test.md", }); expect(report.filesChecked).toBe(1); }); it("should validate multiple files", async () => { await storage.addFile({ filePath: "file1.md", content: "# Title\n## Section\nContent", title: "File 1", }); await storage.addFile({ filePath: "file2.md", content: "# Title\n### Skipped\nContent", title: "File 2", }); const validator = new StructureValidator(storage); const report = await validator.validateStructure(); expect(report.filesChecked).toBe(2); }); }); describe("Full Pipeline Integration", () => { it("should handle complete indexing pipeline", async () => { const content = `--- title: Complete Test author: Test Author --- # Main Title This is test content with [a link](other.md). ## Section One More content here. <!-- TODO: Add examples --> ## Section Two Final section content.`; // Parse const parsed = parser.parse("test.md", content); // Store file await storage.addFile({ filePath: "test.md", content, title: parsed.metadata.title || "Complete Test", }); // Extract links const fileId = storage.getFiles()[0].id; await linkAnalyzer.extractLinks(fileId, "test.md", content); // Chunk content const chunks = chunker.chunk("test.md", content, parsed.headings); // Verify all components worked const files = storage.getFiles(); expect(files.length).toBe(1); expect(files[0].title).toBe("Complete Test"); const links = storage.getLinks("test.md"); expect(links.length).toBeGreaterThan(0); expect(chunks.length).toBeGreaterThan(0); }); it("should maintain referential integrity", async () => { // Add multiple interconnected files await storage.addFile({ filePath: "index.md", content: "# Index\n[Chapter 1](chapter1.md)\n[Chapter 2](chapter2.md)", title: "Index", }); await storage.addFile({ filePath: "chapter1.md", content: "# Chapter 1\n[Back to Index](index.md)\n[Next](chapter2.md)", title: "Chapter 1", }); await storage.addFile({ filePath: "chapter2.md", content: "# Chapter 2\n[Back to Index](index.md)\n[Previous](chapter1.md)", title: "Chapter 2", }); // Extract all links const files = storage.getFiles(); for (const file of files) { const content = file.title; // Simplified for test await linkAnalyzer.extractLinks(file.id, file.filePath, content); } // Verify link graph const indexLinks = storage.getLinks("index.md"); expect(indexLinks.length).toBeGreaterThan(0); const chapter1Links = storage.getLinks("chapter1.md"); expect(chapter1Links.length).toBeGreaterThan(0); const chapter2Links = storage.getLinks("chapter2.md"); expect(chapter2Links.length).toBeGreaterThan(0); }); }); describe("Error Handling Integration", () => { it("should handle missing files gracefully", async () => { const validator = new StructureValidator(storage); const report = await validator.validateStructure({ filePath: "nonexistent.md", }); expect(report.filesChecked).toBe(0); expect(report.issues.length).toBe(0); }); it("should handle empty content", async () => { await storage.addFile({ filePath: "empty.md", content: "", title: "Empty", }); const checker = new TerminologyChecker(storage); const report = await checker.checkTerminology({ autoDetect: true }); // Should not crash expect(report).toBeDefined(); }); it("should handle malformed markdown", async () => { const content = "# Unclosed **bold\n### Random ### headings\n"; const parsed = parser.parse("malformed.md", content); // Should still parse something expect(parsed).toBeDefined(); expect(parsed.headings.length).toBeGreaterThan(0); }); }); describe("Performance Integration", () => { it("should handle multiple files efficiently", async () => { const startTime = Date.now(); // Add 50 files for (let i = 1; i <= 50; i++) { await storage.addFile({ filePath: `file${i}.md`, content: `# File ${i}\nContent for file ${i}.`, title: `File ${i}`, }); } const elapsed = Date.now() - startTime; // Should complete in reasonable time (< 5 seconds) expect(elapsed).toBeLessThan(5000); const files = storage.getFiles(); expect(files.length).toBe(50); }); it("should handle large content efficiently", async () => { // Create large content (10KB) const largeContent = "# Large File\n" + "word ".repeat(2000); const startTime = Date.now(); await storage.addFile({ filePath: "large.md", content: largeContent, title: "Large File", }); const parsed = parser.parse("large.md", largeContent); const chunks = chunker.chunk("large.md", largeContent, parsed.headings); const elapsed = Date.now() - startTime; // Should complete in reasonable time (< 2 seconds) expect(elapsed).toBeLessThan(2000); expect(chunks.length).toBeGreaterThan(0); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/xiaolai/claude-writers-aid-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server