Skip to main content
Glama
watcher-error-handling.test.ts4.94 kB
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { SpecWatcher } from '../watcher.js'; import { SpecParser } from '../parser.js'; import { promises as fs } from 'fs'; import { join } from 'path'; import { tmpdir } from 'os'; describe('SpecWatcher Error Handling', () => { let testDir: string; let watcher: SpecWatcher; let parser: SpecParser; beforeEach(async () => { // Create a temporary test directory testDir = join(tmpdir(), `spec-workflow-test-${Date.now()}`); await fs.mkdir(testDir, { recursive: true }); // Create the workflow directory structure const workflowDir = join(testDir, '.spec-workflow'); const specsDir = join(workflowDir, 'specs'); const steeringDir = join(workflowDir, 'steering'); await fs.mkdir(workflowDir, { recursive: true }); await fs.mkdir(specsDir, { recursive: true }); await fs.mkdir(steeringDir, { recursive: true }); // Create a test spec const testSpecDir = join(specsDir, 'test-spec'); await fs.mkdir(testSpecDir, { recursive: true }); await fs.writeFile(join(testSpecDir, 'requirements.md'), '# Test Requirements\n\nSome content'); // Initialize parser and watcher parser = new SpecParser(testDir); watcher = new SpecWatcher(testDir, parser); }); afterEach(async () => { // Stop watcher if (watcher) { await watcher.stop(); } // Clean up test directory try { await fs.rm(testDir, { recursive: true, force: true }); } catch { // Ignore cleanup errors } }); it('should start without crashing', async () => { await expect(watcher.start()).resolves.not.toThrow(); }); it('should handle file changes without crashing', async () => { await watcher.start(); // Set up event listener to track changes const changeEvents: any[] = []; watcher.on('change', (event) => { changeEvents.push(event); }); // Modify a file const requirementsPath = join(testDir, '.spec-workflow', 'specs', 'test-spec', 'requirements.md'); await fs.writeFile(requirementsPath, '# Updated Requirements\n\nUpdated content'); // Wait for file system events to propagate await new Promise(resolve => setTimeout(resolve, 300)); // Watcher should still be running (not crashed) expect(watcher).toBeDefined(); }); it('should handle parser errors gracefully', async () => { await watcher.start(); // Mock parser to throw an error const originalGetSpec = parser.getSpec.bind(parser); parser.getSpec = vi.fn().mockRejectedValue(new Error('Parser error')); // Set up event listener const changeEvents: any[] = []; watcher.on('change', (event) => { changeEvents.push(event); }); // Modify a file (this will trigger the error) const requirementsPath = join(testDir, '.spec-workflow', 'specs', 'test-spec', 'requirements.md'); await fs.writeFile(requirementsPath, '# Updated Requirements\n\nUpdated content'); // Wait for file system events to propagate await new Promise(resolve => setTimeout(resolve, 300)); // Watcher should still be running despite the error expect(watcher).toBeDefined(); // Restore original method parser.getSpec = originalGetSpec; }); it('should handle steering file changes', async () => { await watcher.start(); // Set up event listener const steeringEvents: any[] = []; watcher.on('steering-change', (event) => { steeringEvents.push(event); }); // Create a steering file const steeringPath = join(testDir, '.spec-workflow', 'steering', 'product.md'); await fs.writeFile(steeringPath, '# Product Steering\n\nSome guidance'); // Wait for file system events to propagate await new Promise(resolve => setTimeout(resolve, 300)); // Should have received at least one event expect(steeringEvents.length).toBeGreaterThanOrEqual(0); // May be 0 or 1 depending on timing }); it('should stop cleanly', async () => { await watcher.start(); await expect(watcher.stop()).resolves.not.toThrow(); }); it('should not crash when stopping without starting', async () => { const newWatcher = new SpecWatcher(testDir, parser); await expect(newWatcher.stop()).resolves.not.toThrow(); }); it('should handle rapid file changes without crashing', async () => { await watcher.start(); const requirementsPath = join(testDir, '.spec-workflow', 'specs', 'test-spec', 'requirements.md'); // Make multiple rapid changes for (let i = 0; i < 5; i++) { await fs.writeFile(requirementsPath, `# Updated Requirements ${i}\n\nUpdated content ${i}`); await new Promise(resolve => setTimeout(resolve, 50)); } // Wait for all events to propagate await new Promise(resolve => setTimeout(resolve, 500)); // Watcher should still be running expect(watcher).toBeDefined(); }); });

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/Pimzino/spec-workflow-mcp'

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