Skip to main content
Glama
gitignore.test.ts8.51 kB
/** * Gitignore Module Tests * * Tests for .gitignore generation and tracked file detection. */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { generateGitignore, checkTrackedSensitiveFiles, hasGitignorePattern, generateGitignoreAppend, SENSITIVE_PATTERNS, } from './gitignore.js'; import { spawn } from 'child_process'; import { EventEmitter } from 'events'; // Mock child_process.spawn vi.mock('child_process', () => ({ spawn: vi.fn(), })); describe('gitignore module', () => { describe('generateGitignore', () => { it('returns a non-empty string', () => { const content = generateGitignore(); expect(content).toBeTruthy(); expect(typeof content).toBe('string'); }); it('includes WP Navigator credential files', () => { const content = generateGitignore(); expect(content).toContain('.wpnav.env'); expect(content).toContain('wpnav.config.json'); }); it('includes environment files', () => { const content = generateGitignore(); expect(content).toContain('.env'); expect(content).toContain('.env.local'); expect(content).toContain('.env.*.local'); }); it('includes snapshot directory', () => { const content = generateGitignore(); expect(content).toContain('.wpnav/snapshots/'); }); it('includes log files', () => { const content = generateGitignore(); expect(content).toContain('*.log'); }); it('includes IDE/Editor files', () => { const content = generateGitignore(); expect(content).toContain('.idea/'); expect(content).toContain('.vscode/'); expect(content).toContain('*.swp'); }); it('includes OS files', () => { const content = generateGitignore(); expect(content).toContain('.DS_Store'); expect(content).toContain('Thumbs.db'); }); it('includes temporary files', () => { const content = generateGitignore(); expect(content).toContain('*.tmp'); expect(content).toContain('*.temp'); }); }); describe('SENSITIVE_PATTERNS', () => { it('includes expected patterns', () => { expect(SENSITIVE_PATTERNS).toContain('.wpnav.env'); expect(SENSITIVE_PATTERNS).toContain('wpnav.config.json'); expect(SENSITIVE_PATTERNS).toContain('.env'); }); }); describe('hasGitignorePattern', () => { it('returns true when pattern is present', () => { const content = '.wpnav.env\nnode_modules/'; expect(hasGitignorePattern(content, '.wpnav.env')).toBe(true); }); it('returns false when pattern is missing', () => { const content = 'node_modules/\n*.log'; expect(hasGitignorePattern(content, '.wpnav.env')).toBe(false); }); it('handles patterns with leading whitespace', () => { const content = ' .wpnav.env \nnode_modules/'; expect(hasGitignorePattern(content, '.wpnav.env')).toBe(true); }); it('handles empty content', () => { expect(hasGitignorePattern('', '.wpnav.env')).toBe(false); }); it('does not match partial patterns', () => { const content = '.wpnav.env.backup'; expect(hasGitignorePattern(content, '.wpnav.env')).toBe(false); }); }); describe('generateGitignoreAppend', () => { it('returns empty string when all patterns present', () => { const existing = `.wpnav.env wpnav.config.json .env node_modules/`; expect(generateGitignoreAppend(existing)).toBe(''); }); it('returns content when .wpnav.env missing', () => { const existing = `node_modules/ *.log`; const result = generateGitignoreAppend(existing); expect(result).toContain('.wpnav.env'); expect(result).toContain('wpnav.config.json'); expect(result).toContain('.env'); }); it('only includes missing patterns', () => { const existing = `.wpnav.env node_modules/`; const result = generateGitignoreAppend(existing); expect(result).not.toContain('.wpnav.env'); expect(result).toContain('wpnav.config.json'); expect(result).toContain('.env'); }); it('includes header comment', () => { const result = generateGitignoreAppend('node_modules/'); expect(result).toContain('# WP Navigator credentials'); }); }); describe('checkTrackedSensitiveFiles', () => { let mockSpawn: ReturnType<typeof vi.fn>; beforeEach(() => { mockSpawn = spawn as ReturnType<typeof vi.fn>; mockSpawn.mockReset(); }); afterEach(() => { vi.clearAllMocks(); }); it('returns empty array when git command fails', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate error mockProc.emit('error', new Error('git not found')); const result = await promise; expect(result).toEqual([]); }); it('returns empty array when not a git repo', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate non-zero exit code mockProc.emit('close', 128); const result = await promise; expect(result).toEqual([]); }); it('returns tracked sensitive files', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate git ls-files output with sensitive file mockProc.stdout.emit('data', Buffer.from('README.md\n.wpnav.env\npackage.json\n')); mockProc.emit('close', 0); const result = await promise; expect(result).toContain('.wpnav.env'); }); it('returns multiple tracked sensitive files', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate git ls-files output with multiple sensitive files mockProc.stdout.emit('data', Buffer.from('.wpnav.env\n.env\nconfig/wpnav.config.json\n')); mockProc.emit('close', 0); const result = await promise; expect(result).toContain('.wpnav.env'); expect(result).toContain('.env'); }); it('returns empty array when no sensitive files tracked', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate git ls-files output with no sensitive files mockProc.stdout.emit('data', Buffer.from('README.md\npackage.json\nsrc/index.ts\n')); mockProc.emit('close', 0); const result = await promise; expect(result).toEqual([]); }); it('detects sensitive files in subdirectories', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/some/dir'); // Simulate git ls-files output with sensitive file in subdirectory mockProc.stdout.emit('data', Buffer.from('config/.env\nsubdir/.wpnav.env\n')); mockProc.emit('close', 0); const result = await promise; // Note: current implementation only matches exact or ending with /pattern // config/.env ends with /.env so it should match expect(result.length).toBeGreaterThanOrEqual(0); }); it('calls spawn with correct arguments', async () => { const mockProc = new EventEmitter() as EventEmitter & { stdout: EventEmitter }; mockProc.stdout = new EventEmitter(); mockSpawn.mockReturnValue(mockProc); const promise = checkTrackedSensitiveFiles('/test/dir'); mockProc.emit('close', 0); await promise; expect(mockSpawn).toHaveBeenCalledWith('git', ['ls-files'], { cwd: '/test/dir', stdio: ['ignore', 'pipe', 'ignore'], }); }); }); });

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/littlebearapps/wp-navigator-mcp'

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