Compare commits
	
		
			2 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 343f295a13 | |||
| 811d37052b | 
							
								
								
									
										149
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										149
									
								
								index.js
									
									
									
									
									
								
							| @@ -3,6 +3,31 @@ const fsSync = require('fs'); | ||||
| const path = require('path'); | ||||
| const crypto = require('crypto'); | ||||
|  | ||||
| /** | ||||
|  * 工具函数:安全执行异步操作,忽略错误 | ||||
|  * | ||||
|  * @template T | ||||
|  * @param {() => Promise<T>} fn - 要执行的异步函数 | ||||
|  * @param {T} defaultValue - 出错时的默认值 | ||||
|  * @returns {Promise<T>} 函数执行结果或默认值 | ||||
|  */ | ||||
| async function safeExecute(fn, defaultValue = undefined) { | ||||
|   try { | ||||
|     return await fn(); | ||||
|   } catch (error) { | ||||
|     return defaultValue; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 工具函数:安全执行同步操作,忽略错误 | ||||
|  * | ||||
|  * @template T | ||||
|  * @param {() => T} fn - 要执行的同步函数 | ||||
|  * @param {T} defaultValue - 出错时的默认值 | ||||
|  * @returns {T} 函数执行结果或默认值 | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * 错误类型常量. | ||||
|  * | ||||
| @@ -191,8 +216,10 @@ class FileDiscovery { | ||||
|     const files = new Set(); | ||||
|  | ||||
|     for (const pattern of patterns) { | ||||
|       const matchedFiles = await this.expandGlob(pattern.trim()); | ||||
|       matchedFiles.forEach(file => files.add(file)); | ||||
|       await safeExecute(async () => { | ||||
|         const matchedFiles = await this.expandGlob(pattern.trim()); | ||||
|         matchedFiles.forEach(file => files.add(file)); | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     return Array.from(files).sort(); | ||||
| @@ -240,17 +267,16 @@ class FileDiscovery { | ||||
|   async expandGlob(pattern) { | ||||
|     // 如果是普通文件路径,直接返回 | ||||
|     if (!pattern.includes('*') && !pattern.includes('?')) { | ||||
|       try { | ||||
|         const stats = await fs.stat(pattern); | ||||
|       const stats = await safeExecute(() => fs.stat(pattern)); | ||||
|       if (stats) { | ||||
|         if (stats.isFile()) { | ||||
|           return [pattern]; | ||||
|         } else if (stats.isDirectory()) { | ||||
|           // 如果是目录,返回目录下所有文件 | ||||
|           return await this.getAllFilesInDirectory(pattern); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         return []; | ||||
|       } | ||||
|       return []; | ||||
|     } | ||||
|  | ||||
|     // 处理 glob 模式 | ||||
| @@ -267,26 +293,22 @@ class FileDiscovery { | ||||
|   async getAllFilesInDirectory(dirPath, includeHidden = false) { | ||||
|     const files = []; | ||||
|  | ||||
|     try { | ||||
|       const entries = await fs.readdir(dirPath, { withFileTypes: true }); | ||||
|     const entries = await safeExecute(() => fs.readdir(dirPath, { withFileTypes: true }), []); | ||||
|  | ||||
|       for (const entry of entries) { | ||||
|         const fullPath = path.join(dirPath, entry.name); | ||||
|     for (const entry of entries) { | ||||
|       const fullPath = path.join(dirPath, entry.name); | ||||
|  | ||||
|         // 默认忽略隐藏文件(以.开头的文件和目录) | ||||
|         if (!includeHidden && entry.name.startsWith('.')) { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         if (entry.isFile()) { | ||||
|           files.push(fullPath); | ||||
|         } else if (entry.isDirectory()) { | ||||
|           const subFiles = await this.getAllFilesInDirectory(fullPath, includeHidden); | ||||
|           files.push(...subFiles); | ||||
|         } | ||||
|       // 默认忽略隐藏文件(以.开头的文件和目录) | ||||
|       if (!includeHidden && entry.name.startsWith('.')) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       if (entry.isFile()) { | ||||
|         files.push(fullPath); | ||||
|       } else if (entry.isDirectory()) { | ||||
|         const subFiles = await this.getAllFilesInDirectory(fullPath, includeHidden); | ||||
|         files.push(...subFiles); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       // 忽略无法访问的目录 | ||||
|     } | ||||
|  | ||||
|     return files; | ||||
| @@ -312,21 +334,17 @@ class FileDiscovery { | ||||
|     } | ||||
|  | ||||
|     // 处理简单的文件名通配符 | ||||
|     try { | ||||
|       const entries = await fs.readdir(dir, { withFileTypes: true }); | ||||
|     const entries = await safeExecute(() => fs.readdir(dir, { withFileTypes: true }), []); | ||||
|  | ||||
|       for (const entry of entries) { | ||||
|         // 默认忽略隐藏文件(以.开头的文件) | ||||
|         if (entry.name.startsWith('.')) { | ||||
|           continue; | ||||
|         } | ||||
|  | ||||
|         if (entry.isFile() && this.matchPattern(entry.name, filename)) { | ||||
|           files.push(path.join(dir, entry.name)); | ||||
|         } | ||||
|     for (const entry of entries) { | ||||
|       // 默认忽略隐藏文件(以.开头的文件) | ||||
|       if (entry.name.startsWith('.')) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       if (entry.isFile() && this.matchPattern(entry.name, filename)) { | ||||
|         files.push(path.join(dir, entry.name)); | ||||
|       } | ||||
|     } catch (error) { | ||||
|       // 目录不存在或无法访问 | ||||
|     } | ||||
|  | ||||
|     return files; | ||||
| @@ -348,17 +366,17 @@ class FileDiscovery { | ||||
|       const basePath = parts[0].replace(/\*+$/, '').replace(/\/$/, '') || '.'; | ||||
|       const remainingPattern = parts[1].replace(/^\//, ''); | ||||
|  | ||||
|       try { | ||||
|         const allFiles = await this.getAllFilesInDirectory(basePath); | ||||
|       const allFiles = await safeExecute(() => this.getAllFilesInDirectory(basePath), []); | ||||
|  | ||||
|         for (const file of allFiles) { | ||||
|           const relativePath = path.relative(basePath, file); | ||||
|           if (this.matchPattern(relativePath, remainingPattern)) { | ||||
|             files.push(file); | ||||
|           } | ||||
|       for (const file of allFiles) { | ||||
|         const relativePath = path.relative(basePath, file); | ||||
|         // 对于 **/pattern 模式,匹配相对路径的末尾部分 | ||||
|         if (remainingPattern && (relativePath === remainingPattern || relativePath.endsWith('/' + remainingPattern))) { | ||||
|           files.push(file); | ||||
|         } else if (this.matchPattern(relativePath, remainingPattern)) { | ||||
|           // 对于包含通配符的模式,使用 matchPattern | ||||
|           files.push(file); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         // 忽略错误 | ||||
|       } | ||||
|     } else { | ||||
|       // 处理单层通配符 | ||||
| @@ -385,34 +403,30 @@ class FileDiscovery { | ||||
|     const [currentPart, ...remainingParts] = parts; | ||||
|     const files = []; | ||||
|  | ||||
|     try { | ||||
|       const entries = await fs.readdir(currentPath, { withFileTypes: true }); | ||||
|     const entries = await safeExecute(() => fs.readdir(currentPath, { withFileTypes: true }), []); | ||||
|  | ||||
|       for (const entry of entries) { | ||||
|         // 默认忽略隐藏文件和目录(以.开头的) | ||||
|         if (entry.name.startsWith('.')) { | ||||
|           continue; | ||||
|         } | ||||
|     for (const entry of entries) { | ||||
|       // 默认忽略隐藏文件和目录(以.开头的) | ||||
|       if (entry.name.startsWith('.')) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|         const fullPath = path.join(currentPath, entry.name); | ||||
|       const fullPath = path.join(currentPath, entry.name); | ||||
|  | ||||
|         if (this.matchPattern(entry.name, currentPart)) { | ||||
|           if (remainingParts.length === 0) { | ||||
|             // 最后一部分,检查是否为文件 | ||||
|             if (entry.isFile()) { | ||||
|               files.push(fullPath); | ||||
|             } | ||||
|           } else { | ||||
|             // 还有更多部分,递归处理 | ||||
|             if (entry.isDirectory()) { | ||||
|               const subFiles = await this.matchPatternParts(remainingParts, fullPath); | ||||
|               files.push(...subFiles); | ||||
|             } | ||||
|       if (this.matchPattern(entry.name, currentPart)) { | ||||
|         if (remainingParts.length === 0) { | ||||
|           // 最后一部分,检查是否为文件 | ||||
|           if (entry.isFile()) { | ||||
|             files.push(fullPath); | ||||
|           } | ||||
|         } else { | ||||
|           // 还有更多部分,递归处理 | ||||
|           if (entry.isDirectory()) { | ||||
|             const subFiles = await this.matchPatternParts(remainingParts, fullPath); | ||||
|             files.push(...subFiles); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } catch (error) { | ||||
|       // 忽略错误 | ||||
|     } | ||||
|  | ||||
|     return files; | ||||
| @@ -686,4 +700,5 @@ module.exports = { | ||||
|   ActionOutputs, | ||||
|   ActionError, | ||||
|   ErrorType, | ||||
|   safeExecute, | ||||
| }; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "files-hash-action", | ||||
|   "version": "0.1.0", | ||||
|   "version": "0.1.1", | ||||
|   "description": "A lightweight GitHub Action to calculate hash of multiple files", | ||||
|   "main": "index.js", | ||||
|   "scripts": { | ||||
|   | ||||
| @@ -76,125 +76,248 @@ describe('测试 FileDiscovery 类', () => { | ||||
|       expect(files[0]).toBe('test-dir/file1.js'); | ||||
|     }); | ||||
|  | ||||
|     it('应该使用通配符模式查找文件', async () => { | ||||
|     it('应该找到通配符(*.js)形式的文件', async () => { | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       const files = await discovery.findFiles(['test-dir/*.js']); | ||||
|       expect(files).toHaveLength(1); | ||||
|     }); | ||||
|  | ||||
|     it('应该递归查找文件', async () => { | ||||
|     it('应该找到递归通配符(**/*.js)形式的文件', async () => { | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       const files = await discovery.findFiles(['test-dir/**/*.js']); | ||||
|       expect(files).toHaveLength(2); | ||||
|     }); | ||||
|  | ||||
|     it('应该处理空目录', async () => { | ||||
|     it('应该找到递归通配符开头与文件名(**/config.json)形式的文件', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       const files = await discovery.findFiles([tempDir]); | ||||
|       // 创建多个目录层级的同名文件 | ||||
|       const configFiles = [ | ||||
|         path.join(tempDir, 'config.json'), | ||||
|         path.join(tempDir, 'src', 'config.json'), | ||||
|         path.join(tempDir, 'src', 'utils', 'config.json'), | ||||
|         path.join(tempDir, 'lib', 'config.json'), | ||||
|         path.join(tempDir, 'test', 'config.json'), | ||||
|       ]; | ||||
|  | ||||
|       expect(files).toEqual([]); | ||||
|       cleanupTestDirs(tempDir); | ||||
|     }); | ||||
|       // 创建一些其他文件用于验证不会误匹配 | ||||
|       const otherFiles = [ | ||||
|         path.join(tempDir, 'src', 'app.js'), | ||||
|         path.join(tempDir, 'src', 'utils', 'helper.js'), | ||||
|         path.join(tempDir, 'lib', 'main.js'), | ||||
|         path.join(tempDir, 'test', 'test.js'), | ||||
|       ]; | ||||
|  | ||||
|     it('应该处理无效模式', async () => { | ||||
|       const discovery = new FileDiscovery(); | ||||
|       const files = await discovery.findFiles(['nonexistent/**/*.txt']); | ||||
|  | ||||
|       expect(files).toEqual([]); | ||||
|     }); | ||||
|  | ||||
|     it('应该处理混合有效和无效模式', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const testFile = path.join(tempDir, 'test.txt'); | ||||
|       await createTestFile(testFile, 'content'); | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '*.txt'), 'nonexistent/**/*.txt']); | ||||
|  | ||||
|       expect(files).toContain(testFile); | ||||
|       cleanupTestFiles([testFile]); | ||||
|       cleanupTestDirs(tempDir); | ||||
|     }); | ||||
|  | ||||
|     it('应该处理无权限的目录', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const restrictedDir = path.join(tempDir, 'restricted'); | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       try { | ||||
|         fs.mkdirSync(restrictedDir, { mode: 0o000 }); | ||||
|  | ||||
|         const files = await discovery.findFiles([restrictedDir + '/*.txt']); | ||||
|  | ||||
|         expect(files).toEqual([]); | ||||
|       } catch (error) { | ||||
|         // 某些系统可能不允许创建无权限目录,这是预期的 | ||||
|         expect(error.code).toBe('EACCES'); | ||||
|       } finally { | ||||
|         // 清理:先恢复权限再删除 | ||||
|         try { | ||||
|           fs.chmodSync(restrictedDir, 0o755); | ||||
|           cleanupTestDirs(tempDir); | ||||
|         } catch (cleanupError) { | ||||
|           // 忽略清理错误 | ||||
|         } | ||||
|       // 创建所有文件 | ||||
|       for (const file of [...configFiles, ...otherFiles]) { | ||||
|         fs.mkdirSync(path.dirname(file), { recursive: true }); | ||||
|         await createTestFile(file, '{"test": "content"}'); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     it('应该忽略隐藏文件(默认)', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const hiddenFile = path.join(tempDir, '.hidden.txt'); | ||||
|       const visibleFile = path.join(tempDir, 'visible.txt'); | ||||
|       const discovery = new FileDiscovery(); | ||||
|       // 测试 tempDir 目录下的 **/config.json 模式(限制在测试目录内) | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '**/config.json')]); | ||||
|  | ||||
|       await createTestFile(hiddenFile, 'hidden content'); | ||||
|       await createTestFile(visibleFile, 'visible content'); | ||||
|       // 应该找到所有的 config.json 文件 | ||||
|       expect(files).toHaveLength(configFiles.length); | ||||
|       for (const configFile of configFiles) { | ||||
|         expect(files).toContain(configFile); | ||||
|       } | ||||
|  | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '*.txt')]); | ||||
|  | ||||
|       expect(files).toContain(visibleFile); | ||||
|       expect(files).not.toContain(hiddenFile); | ||||
|  | ||||
|       cleanupTestFiles(hiddenFile, visibleFile); | ||||
|       // 清理测试文件 | ||||
|       cleanupTestFiles([...configFiles, ...otherFiles]); | ||||
|       cleanupTestDirs(tempDir); | ||||
|     }); | ||||
|  | ||||
|     it('应该处理空文件', async () => { | ||||
|     it('应该找到递归通配符开头与扩展名(**/*.md)形式的文件', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const emptyFile = path.join(tempDir, 'empty.txt'); | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       await createTestFile(emptyFile, ''); | ||||
|       // 创建多层目录结构中的 .md 文件 | ||||
|       const mdFiles = [ | ||||
|         path.join(tempDir, 'README.md'), | ||||
|         path.join(tempDir, 'docs', 'guide.md'), | ||||
|         path.join(tempDir, 'src', 'docs', 'api.md'), | ||||
|         path.join(tempDir, 'lib', 'components', 'button.md'), | ||||
|         path.join(tempDir, 'test', 'integration', 'tests.md'), | ||||
|       ]; | ||||
|  | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '*.txt')]); | ||||
|       // 创建一些其他扩展名的文件用于验证不会误匹配 | ||||
|       const otherFiles = [ | ||||
|         path.join(tempDir, 'src', 'main.js'), | ||||
|         path.join(tempDir, 'src', 'styles.css'), | ||||
|         path.join(tempDir, 'docs', 'config.json'), | ||||
|         path.join(tempDir, 'test', 'test.py'), | ||||
|       ]; | ||||
|  | ||||
|       expect(files).toContain(emptyFile); | ||||
|       // 创建所有文件 | ||||
|       for (const file of [...mdFiles, ...otherFiles]) { | ||||
|         fs.mkdirSync(path.dirname(file), { recursive: true }); | ||||
|         await createTestFile(file, '# Test Content'); | ||||
|       } | ||||
|  | ||||
|       cleanupTestFiles(emptyFile); | ||||
|       // 测试 tempDir 目录下的 **/*.md 模式(限制在测试目录内) | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '**/*.md')]); | ||||
|  | ||||
|       // 应该找到所有的 .md 文件 | ||||
|       expect(files).toHaveLength(mdFiles.length); | ||||
|       for (const mdFile of mdFiles) { | ||||
|         expect(files).toContain(mdFile); | ||||
|       } | ||||
|  | ||||
|       // 清理测试文件 | ||||
|       cleanupTestFiles([...mdFiles, ...otherFiles]); | ||||
|       cleanupTestDirs(tempDir); | ||||
|     }); | ||||
|  | ||||
|     it('应该处理嵌套很深的目录', async () => { | ||||
|     it('应该正确处理递归通配符前缀的复杂模式', async () => { | ||||
|       const tempDir = await createTestDir(); | ||||
|       const deepFile = path.join(tempDir, 'level1', 'level2', 'level3', 'deep.txt'); | ||||
|       const discovery = new FileDiscovery(); | ||||
|  | ||||
|       fs.mkdirSync(path.dirname(deepFile), { recursive: true }); | ||||
|       await createTestFile(deepFile, 'deep content'); | ||||
|       // 创建测试文件结构 | ||||
|       const testFiles = [ | ||||
|         path.join(tempDir, 'src', 'components', 'Button.js'), | ||||
|         path.join(tempDir, 'src', 'components', 'Input.js'), | ||||
|         path.join(tempDir, 'src', 'utils', 'helpers.js'), | ||||
|         path.join(tempDir, 'lib', 'vendor', 'jquery.js'), | ||||
|         path.join(tempDir, 'test', 'unit', 'Button.test.js'), | ||||
|         path.join(tempDir, 'test', 'integration', 'App.test.js'), | ||||
|       ]; | ||||
|  | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '**/*.txt')]); | ||||
|       // 创建一些非 JS 文件 | ||||
|       const nonJSFiles = [ | ||||
|         path.join(tempDir, 'src', 'styles.css'), | ||||
|         path.join(tempDir, 'docs', 'README.md'), | ||||
|         path.join(tempDir, 'config.json'), | ||||
|       ]; | ||||
|  | ||||
|       expect(files).toContain(deepFile); | ||||
|       // 创建所有文件 | ||||
|       for (const file of [...testFiles, ...nonJSFiles]) { | ||||
|         fs.mkdirSync(path.dirname(file), { recursive: true }); | ||||
|         await createTestFile(file, 'console.log("test");'); | ||||
|       } | ||||
|  | ||||
|       cleanupTestFiles(deepFile); | ||||
|       // 测试 tempDir 目录下的 **/*.js 模式(限制在测试目录内) | ||||
|       const files = await discovery.findFiles([path.join(tempDir, '**/*.js')]); | ||||
|  | ||||
|       // 应该找到所有的 .js 文件 | ||||
|       expect(files).toHaveLength(testFiles.length); | ||||
|       for (const jsFile of testFiles) { | ||||
|         expect(files).toContain(jsFile); | ||||
|       } | ||||
|  | ||||
|       // 清理测试文件 | ||||
|       cleanupTestFiles([...testFiles, ...nonJSFiles]); | ||||
|       cleanupTestDirs(tempDir); | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理空目录', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     const files = await discovery.findFiles([tempDir]); | ||||
|  | ||||
|     expect(files).toEqual([]); | ||||
|     cleanupTestDirs(tempDir); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理无效模式', async () => { | ||||
|     const discovery = new FileDiscovery(); | ||||
|     const files = await discovery.findFiles(['nonexistent/**/*.txt']); | ||||
|  | ||||
|     expect(files).toEqual([]); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理混合有效和无效模式', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const testFile = path.join(tempDir, 'test.txt'); | ||||
|     await createTestFile(testFile, 'content'); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     const files = await discovery.findFiles([path.join(tempDir, '*.txt'), 'nonexistent/**/*.txt']); | ||||
|  | ||||
|     expect(files).toContain(testFile); | ||||
|     cleanupTestFiles([testFile]); | ||||
|     cleanupTestDirs(tempDir); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理无权限的目录', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const restrictedDir = path.join(tempDir, 'restricted'); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     try { | ||||
|       fs.mkdirSync(restrictedDir, { mode: 0o000 }); | ||||
|  | ||||
|       const files = await discovery.findFiles([restrictedDir + '/*.txt']); | ||||
|  | ||||
|       expect(files).toEqual([]); | ||||
|     } catch (error) { | ||||
|       // 某些系统可能不允许创建无权限目录,这是预期的 | ||||
|       expect(error.code).toBe('EACCES'); | ||||
|     } finally { | ||||
|       // 清理:先恢复权限再删除 | ||||
|       try { | ||||
|         fs.chmodSync(restrictedDir, 0o755); | ||||
|         cleanupTestDirs(tempDir); | ||||
|       } catch (cleanupError) { | ||||
|         // 忽略清理错误 | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   it('应该忽略隐藏文件(默认)', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const hiddenFile = path.join(tempDir, '.hidden.txt'); | ||||
|     const visibleFile = path.join(tempDir, 'visible.txt'); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     await createTestFile(hiddenFile, 'hidden content'); | ||||
|     await createTestFile(visibleFile, 'visible content'); | ||||
|  | ||||
|     const files = await discovery.findFiles([path.join(tempDir, '*.txt')]); | ||||
|  | ||||
|     expect(files).toContain(visibleFile); | ||||
|     expect(files).not.toContain(hiddenFile); | ||||
|  | ||||
|     cleanupTestFiles(hiddenFile, visibleFile); | ||||
|     cleanupTestDirs(tempDir); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理空文件', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const emptyFile = path.join(tempDir, 'empty.txt'); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     await createTestFile(emptyFile, ''); | ||||
|  | ||||
|     const files = await discovery.findFiles([path.join(tempDir, '*.txt')]); | ||||
|  | ||||
|     expect(files).toContain(emptyFile); | ||||
|  | ||||
|     cleanupTestFiles(emptyFile); | ||||
|     cleanupTestDirs(tempDir); | ||||
|   }); | ||||
|  | ||||
|   it('应该处理嵌套很深的目录', async () => { | ||||
|     const tempDir = await createTestDir(); | ||||
|     const deepFile = path.join(tempDir, 'level1', 'level2', 'level3', 'deep.txt'); | ||||
|     const discovery = new FileDiscovery(); | ||||
|  | ||||
|     fs.mkdirSync(path.dirname(deepFile), { recursive: true }); | ||||
|     await createTestFile(deepFile, 'deep content'); | ||||
|  | ||||
|     const files = await discovery.findFiles([path.join(tempDir, '**/*.txt')]); | ||||
|  | ||||
|     expect(files).toContain(deepFile); | ||||
|  | ||||
|     cleanupTestFiles(deepFile); | ||||
|     cleanupTestDirs(tempDir); | ||||
|   }); | ||||
|  | ||||
|   describe('测试 FileDiscovery.validateFiles 方法', () => { | ||||
|     const existingFile = 'existing-file.txt'; | ||||
|     const nonExistingFile = 'non-existing-file.txt'; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user