@amirdaraee/namewise 0.5.3 → 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +60 -60
  3. package/dist/index.js +0 -0
  4. package/dist/services/claude-service.d.ts.map +1 -1
  5. package/dist/services/claude-service.js +3 -0
  6. package/dist/services/claude-service.js.map +1 -1
  7. package/dist/services/lmstudio-service.d.ts +1 -0
  8. package/dist/services/lmstudio-service.d.ts.map +1 -1
  9. package/dist/services/lmstudio-service.js +16 -1
  10. package/dist/services/lmstudio-service.js.map +1 -1
  11. package/dist/services/ollama-service.d.ts +1 -0
  12. package/dist/services/ollama-service.d.ts.map +1 -1
  13. package/dist/services/ollama-service.js +16 -1
  14. package/dist/services/ollama-service.js.map +1 -1
  15. package/dist/services/openai-service.d.ts.map +1 -1
  16. package/dist/services/openai-service.js +3 -0
  17. package/dist/services/openai-service.js.map +1 -1
  18. package/package.json +8 -8
  19. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -82
  20. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -61
  21. package/.github/workflows/auto-release.yml +0 -81
  22. package/.github/workflows/build.yml +0 -55
  23. package/.github/workflows/publish.yml +0 -134
  24. package/.github/workflows/test.yml +0 -45
  25. package/eng.traineddata +0 -0
  26. package/src/cli/commands.ts +0 -64
  27. package/src/cli/rename.ts +0 -171
  28. package/src/index.ts +0 -54
  29. package/src/parsers/excel-parser.ts +0 -66
  30. package/src/parsers/factory.ts +0 -38
  31. package/src/parsers/pdf-parser.ts +0 -99
  32. package/src/parsers/text-parser.ts +0 -43
  33. package/src/parsers/word-parser.ts +0 -50
  34. package/src/services/ai-factory.ts +0 -39
  35. package/src/services/claude-service.ts +0 -119
  36. package/src/services/file-renamer.ts +0 -141
  37. package/src/services/lmstudio-service.ts +0 -161
  38. package/src/services/ollama-service.ts +0 -191
  39. package/src/services/openai-service.ts +0 -117
  40. package/src/types/index.ts +0 -76
  41. package/src/types/pdf-extraction.d.ts +0 -7
  42. package/src/utils/ai-prompts.ts +0 -76
  43. package/src/utils/file-templates.ts +0 -275
  44. package/src/utils/naming-conventions.ts +0 -67
  45. package/src/utils/pdf-to-image.ts +0 -137
  46. package/tests/data/console-test-1.txt +0 -1
  47. package/tests/data/console-test-2.txt +0 -1
  48. package/tests/data/console-test-long-filename-for-display-testing.txt +0 -1
  49. package/tests/data/empty-file.txt +0 -0
  50. package/tests/data/failure.txt +0 -1
  51. package/tests/data/file1.txt +0 -1
  52. package/tests/data/file2.txt +0 -1
  53. package/tests/data/much-longer-filename-to-test-clearing.txt +0 -1
  54. package/tests/data/sample-markdown.md +0 -9
  55. package/tests/data/sample-pdf.pdf +0 -0
  56. package/tests/data/sample-text.txt +0 -25
  57. package/tests/data/short.txt +0 -1
  58. package/tests/data/single-file.txt +0 -1
  59. package/tests/data/success.txt +0 -1
  60. package/tests/data/this-is-a-very-long-filename-that-should-be-truncated-for-better-display-purposes.txt +0 -1
  61. package/tests/data/very-long-filename-that-should-be-cleared-properly.txt +0 -1
  62. package/tests/data/x.txt +0 -1
  63. package/tests/integration/ai-prompting.test.ts +0 -386
  64. package/tests/integration/end-to-end.test.ts +0 -209
  65. package/tests/integration/person-name-extraction.test.ts +0 -440
  66. package/tests/integration/workflow.test.ts +0 -336
  67. package/tests/mocks/mock-ai-service.ts +0 -58
  68. package/tests/unit/cli/commands.test.ts +0 -169
  69. package/tests/unit/parsers/factory.test.ts +0 -100
  70. package/tests/unit/parsers/pdf-parser.test.ts +0 -63
  71. package/tests/unit/parsers/text-parser.test.ts +0 -85
  72. package/tests/unit/services/ai-factory.test.ts +0 -85
  73. package/tests/unit/services/claude-service.test.ts +0 -188
  74. package/tests/unit/services/file-renamer.test.ts +0 -514
  75. package/tests/unit/services/lmstudio-service.test.ts +0 -326
  76. package/tests/unit/services/ollama-service.test.ts +0 -264
  77. package/tests/unit/services/openai-service.test.ts +0 -196
  78. package/tests/unit/utils/ai-prompts.test.ts +0 -213
  79. package/tests/unit/utils/file-templates.test.ts +0 -199
  80. package/tests/unit/utils/naming-conventions.test.ts +0 -88
  81. package/tests/unit/utils/pdf-to-image.test.ts +0 -127
  82. package/tsconfig.json +0 -20
  83. package/vitest.config.ts +0 -30
@@ -1,386 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { FileRenamer } from '../../src/services/file-renamer.js';
3
- import { DocumentParserFactory } from '../../src/parsers/factory.js';
4
- import { AIProvider, FileInfo, Config } from '../../src/types/index.js';
5
-
6
- // Mock AI Provider that captures prompts for testing
7
- class MockAIProvider implements AIProvider {
8
- name = 'MockAI';
9
- public capturedPrompts: string[] = [];
10
- public capturedContext: any[] = [];
11
-
12
- async generateFileName(
13
- content: string,
14
- originalName: string,
15
- namingConvention = 'kebab-case',
16
- category = 'general',
17
- fileInfo?: FileInfo
18
- ): Promise<string> {
19
- // Capture the prompt for analysis (we can't see the actual prompt here, but we can test the result)
20
- this.capturedContext.push({
21
- content,
22
- originalName,
23
- namingConvention,
24
- category,
25
- fileInfo
26
- });
27
-
28
- // Mock intelligent responses based on content (AI generates core name, template applies later)
29
- if (content.includes('Setareh') && content.includes('visa')) {
30
- return 'setareh-visitor-visa-application-for-family-members-in-canada';
31
- }
32
-
33
- if (content.includes('John Doe') && content.includes('contract')) {
34
- return 'john-doe-employment-contract-software-engineer';
35
- }
36
-
37
- if (content.includes('Maria') && content.includes('wedding')) {
38
- return 'maria-wedding-ceremony-invitation-june-2024';
39
- }
40
-
41
- // Default response
42
- return 'generic-document-filename';
43
- }
44
- }
45
-
46
- describe('AI Prompting Integration Tests', () => {
47
- let mockAI: MockAIProvider;
48
- let fileRenamer: FileRenamer;
49
- let config: Config;
50
-
51
- beforeEach(() => {
52
- mockAI = new MockAIProvider();
53
- const parserFactory = new DocumentParserFactory();
54
-
55
- config = {
56
- provider: 'claude',
57
- apiKey: 'test-key',
58
- maxFileSize: 10 * 1024 * 1024,
59
- namingConvention: 'kebab-case',
60
- templateOptions: {
61
- category: 'document',
62
- personalName: 'TestUser',
63
- dateFormat: 'YYYY-MM-DD'
64
- },
65
- dryRun: true
66
- };
67
-
68
- fileRenamer = new FileRenamer(parserFactory, mockAI, config);
69
- });
70
-
71
- describe('Person Name Detection and Placement', () => {
72
- it('should place person names at the beginning when detected in content', async () => {
73
- const fileInfo: FileInfo = {
74
- name: 'visa-application.pdf',
75
- path: '/no/visa-application.pdf', // Note: irrelevant folder name "no"
76
- extension: '.pdf',
77
- size: 1024 * 50,
78
- createdAt: new Date('2024-01-15'),
79
- modifiedAt: new Date('2024-02-01'),
80
- parentFolder: 'no', // Should be ignored
81
- folderPath: ['home', 'documents', 'no']
82
- };
83
-
84
- // Mock the parser to return content with person name
85
- const mockParser = {
86
- parse: vi.fn().mockResolvedValue({
87
- content: 'Visitor visa application for Setareh Ahmadi and family members to visit Canada. This application includes documentation for tourism purposes.',
88
- metadata: {
89
- title: 'Visa Application Form',
90
- pages: 3
91
- }
92
- })
93
- };
94
-
95
- // Mock the parser factory
96
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
97
-
98
- const results = await fileRenamer.renameFiles([fileInfo]);
99
-
100
- expect(results[0].success).toBe(true);
101
- // Template applies: {content}-{personalName}-{date}
102
- expect(results[0].suggestedName).toMatch(/setareh-visitor-visa-application-for-family-members-in-canada-testuser-\d{4}-\d{2}-\d{2}\.pdf/);
103
-
104
- // Verify the AI was called with the right context
105
- expect(mockAI.capturedContext).toHaveLength(1);
106
- expect(mockAI.capturedContext[0].content).toContain('Setareh Ahmadi');
107
- expect(mockAI.capturedContext[0].fileInfo.parentFolder).toBe('no');
108
- });
109
-
110
- it('should handle contract documents with person names', async () => {
111
- const fileInfo: FileInfo = {
112
- name: 'employment-contract.pdf',
113
- path: '/contracts/employment-contract.pdf',
114
- extension: '.pdf',
115
- size: 1024 * 75,
116
- createdAt: new Date('2024-01-15'),
117
- modifiedAt: new Date('2024-02-01'),
118
- parentFolder: 'contracts',
119
- folderPath: ['home', 'legal', 'contracts']
120
- };
121
-
122
- const mockParser = {
123
- parse: vi.fn().mockResolvedValue({
124
- content: 'Employment Contract between TechCorp Inc. and John Doe for the position of Senior Software Engineer. This contract outlines terms of employment, salary, and benefits.',
125
- metadata: {
126
- title: 'Employment Agreement',
127
- author: 'Legal Department',
128
- pages: 8
129
- }
130
- })
131
- };
132
-
133
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
134
-
135
- const results = await fileRenamer.renameFiles([fileInfo]);
136
-
137
- expect(results[0].success).toBe(true);
138
- // Template applies: {content}-{personalName}-{date}
139
- expect(results[0].suggestedName).toMatch(/john-doe-employment-contract-software-engineer-testuser-\d{4}-\d{2}-\d{2}\.pdf/);
140
- });
141
-
142
- it('should handle wedding documents with person names', async () => {
143
- const fileInfo: FileInfo = {
144
- name: 'invitation.pdf',
145
- path: '/events/invitation.pdf',
146
- extension: '.pdf',
147
- size: 1024 * 25,
148
- createdAt: new Date('2024-01-15'),
149
- modifiedAt: new Date('2024-02-01'),
150
- parentFolder: 'events',
151
- folderPath: ['home', 'personal', 'events']
152
- };
153
-
154
- const mockParser = {
155
- parse: vi.fn().mockResolvedValue({
156
- content: 'You are cordially invited to the wedding ceremony of Maria Rodriguez and David Thompson on June 15th, 2024 at St. Mary\'s Church.',
157
- metadata: {
158
- title: 'Wedding Invitation',
159
- pages: 1
160
- }
161
- })
162
- };
163
-
164
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
165
-
166
- const results = await fileRenamer.renameFiles([fileInfo]);
167
-
168
- expect(results[0].success).toBe(true);
169
- // Template applies: {content}-{personalName}-{date}
170
- expect(results[0].suggestedName).toMatch(/maria-wedding-ceremony-invitation-june-2024-testuser-\d{4}-\d{2}-\d{2}\.pdf/);
171
- });
172
- });
173
-
174
- describe('Folder Name Filtering', () => {
175
- it('should ignore irrelevant folder names like "no"', async () => {
176
- const fileInfo: FileInfo = {
177
- name: 'document.pdf',
178
- path: '/no/document.pdf',
179
- extension: '.pdf',
180
- size: 1024 * 30,
181
- createdAt: new Date('2024-01-15'),
182
- modifiedAt: new Date('2024-02-01'),
183
- parentFolder: 'no', // This should be ignored by AI
184
- folderPath: ['home', 'downloads', 'no']
185
- };
186
-
187
- const mockParser = {
188
- parse: vi.fn().mockResolvedValue({
189
- content: 'This is a general business report about quarterly sales performance and market analysis.',
190
- metadata: {
191
- title: 'Q4 Sales Report',
192
- pages: 12
193
- }
194
- })
195
- };
196
-
197
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
198
-
199
- const results = await fileRenamer.renameFiles([fileInfo]);
200
-
201
- expect(results[0].success).toBe(true);
202
- // Should not include "no" in the filename, template applies: {content}-{personalName}-{date}
203
- expect(results[0].suggestedName).not.toContain('no');
204
- expect(results[0].suggestedName).toMatch(/generic-document-filename-testuser-\d{4}-\d{2}-\d{2}\.pdf/);
205
-
206
- // Verify the folder context was passed but should be ignored by prompt instructions
207
- expect(mockAI.capturedContext[0].fileInfo.parentFolder).toBe('no');
208
- });
209
-
210
- it('should handle meaningful folder names appropriately', async () => {
211
- const fileInfo: FileInfo = {
212
- name: 'report.pdf',
213
- path: '/financial-reports/report.pdf',
214
- extension: '.pdf',
215
- size: 1024 * 45,
216
- createdAt: new Date('2024-01-15'),
217
- modifiedAt: new Date('2024-02-01'),
218
- parentFolder: 'financial-reports',
219
- folderPath: ['home', 'business', 'financial-reports']
220
- };
221
-
222
- const mockParser = {
223
- parse: vi.fn().mockResolvedValue({
224
- content: 'Annual financial statement showing revenue, expenses, and profit margins for fiscal year 2023.',
225
- metadata: {
226
- title: 'Annual Financial Statement',
227
- pages: 20
228
- }
229
- })
230
- };
231
-
232
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
233
-
234
- const results = await fileRenamer.renameFiles([fileInfo]);
235
-
236
- expect(results[0].success).toBe(true);
237
- // The AI gets the folder context but should focus on content, not folder name
238
- expect(mockAI.capturedContext[0].fileInfo.parentFolder).toBe('financial-reports');
239
- });
240
- });
241
-
242
- describe('Template Integration with AI Prompting', () => {
243
- it('should work with document template and person detection', async () => {
244
- config.templateOptions.category = 'document';
245
- config.templateOptions.personalName = 'TestUser';
246
-
247
- const fileInfo: FileInfo = {
248
- name: 'contract.pdf',
249
- path: '/legal/contract.pdf',
250
- extension: '.pdf',
251
- size: 1024 * 60,
252
- createdAt: new Date('2024-01-15'),
253
- modifiedAt: new Date('2024-02-01'),
254
- parentFolder: 'legal',
255
- folderPath: ['home', 'legal']
256
- };
257
-
258
- const mockParser = {
259
- parse: vi.fn().mockResolvedValue({
260
- content: 'Service agreement between Company ABC and Setareh Ahmadi for consulting services.',
261
- metadata: {
262
- title: 'Service Agreement',
263
- pages: 5
264
- }
265
- })
266
- };
267
-
268
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
269
-
270
- const results = await fileRenamer.renameFiles([fileInfo]);
271
-
272
- expect(results[0].success).toBe(true);
273
- // AI should detect "Setareh" and include it at the beginning, but our mock doesn't match this content
274
- expect(results[0].suggestedName).toMatch(/generic-document-filename-testuser-\d{4}-\d{2}-\d{2}\.pdf/);
275
- });
276
-
277
- it('should pass correct category information to AI', async () => {
278
- config.templateOptions.category = 'movie';
279
-
280
- const fileInfo: FileInfo = {
281
- name: 'film.mp4',
282
- path: '/movies/film.mp4',
283
- extension: '.mp4',
284
- size: 1024 * 1024 * 500, // 500MB
285
- createdAt: new Date('2024-01-15'),
286
- modifiedAt: new Date('2024-02-01'),
287
- parentFolder: 'movies',
288
- folderPath: ['home', 'media', 'movies']
289
- };
290
-
291
- const mockParser = {
292
- parse: vi.fn().mockResolvedValue({
293
- content: 'Movie file metadata or subtitle content here.',
294
- metadata: {}
295
- })
296
- };
297
-
298
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
299
-
300
- // Reduce file size to avoid maxFileSize limit
301
- fileInfo.size = 1024 * 1024; // 1MB instead of 500MB
302
-
303
- const results = await fileRenamer.renameFiles([fileInfo]);
304
-
305
- expect(results[0].success).toBe(true);
306
- expect(mockAI.capturedContext[0].category).toBe('movie');
307
- // Movie template doesn't include personalName, just {content}-{year}
308
- expect(results[0].suggestedName).toBe('generic-document-filename.mp4');
309
- });
310
- });
311
-
312
- describe('Naming Convention Integration', () => {
313
- it('should pass naming convention to AI prompting system', async () => {
314
- config.namingConvention = 'snake_case';
315
-
316
- const fileInfo: FileInfo = {
317
- name: 'test.txt',
318
- path: '/test.txt',
319
- extension: '.txt',
320
- size: 1024,
321
- createdAt: new Date('2024-01-15'),
322
- modifiedAt: new Date('2024-02-01'),
323
- parentFolder: 'root',
324
- folderPath: ['root']
325
- };
326
-
327
- const mockParser = {
328
- parse: vi.fn().mockResolvedValue({
329
- content: 'Test document content.',
330
- metadata: {}
331
- })
332
- };
333
-
334
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
335
-
336
- const results = await fileRenamer.renameFiles([fileInfo]);
337
-
338
- expect(results[0].success).toBe(true);
339
- expect(mockAI.capturedContext[0].namingConvention).toBe('snake_case');
340
- });
341
- });
342
-
343
- describe('Content and Metadata Passing', () => {
344
- it('should pass all relevant metadata to AI service', async () => {
345
- const fileInfo: FileInfo = {
346
- name: 'comprehensive.pdf',
347
- path: '/docs/comprehensive.pdf',
348
- extension: '.pdf',
349
- size: 1024 * 100,
350
- createdAt: new Date('2024-01-15'),
351
- modifiedAt: new Date('2024-02-01'),
352
- parentFolder: 'docs',
353
- folderPath: ['home', 'documents', 'docs'],
354
- documentMetadata: {
355
- title: 'Comprehensive Report',
356
- author: 'Dr. Smith',
357
- creator: 'LaTeX',
358
- subject: 'Research Findings',
359
- keywords: ['research', 'analysis', 'data'],
360
- creationDate: new Date('2024-01-10'),
361
- modificationDate: new Date('2024-01-25'),
362
- pages: 45,
363
- wordCount: 12000
364
- }
365
- };
366
-
367
- const mockParser = {
368
- parse: vi.fn().mockResolvedValue({
369
- content: 'Comprehensive research report on data analysis methodologies and findings.',
370
- metadata: fileInfo.documentMetadata
371
- })
372
- };
373
-
374
- vi.spyOn(fileRenamer['parserFactory'], 'getParser').mockReturnValue(mockParser);
375
-
376
- const results = await fileRenamer.renameFiles([fileInfo]);
377
-
378
- expect(results[0].success).toBe(true);
379
-
380
- const capturedContext = mockAI.capturedContext[0];
381
- expect(capturedContext.fileInfo.documentMetadata.title).toBe('Comprehensive Report');
382
- expect(capturedContext.fileInfo.documentMetadata.author).toBe('Dr. Smith');
383
- expect(capturedContext.fileInfo.documentMetadata.keywords).toEqual(['research', 'analysis', 'data']);
384
- });
385
- });
386
- });
@@ -1,209 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
- import { promises as fs } from 'fs';
3
- import path from 'path';
4
- import { exec } from 'child_process';
5
- import { promisify } from 'util';
6
-
7
- const execAsync = promisify(exec);
8
-
9
- // Mock fs operations for integration tests
10
- vi.mock('fs', async () => {
11
- const actual = await vi.importActual('fs');
12
- return {
13
- ...actual,
14
- promises: {
15
- ...actual.promises,
16
- rename: vi.fn(),
17
- access: vi.fn()
18
- }
19
- };
20
- });
21
-
22
- describe('End-to-End Integration Tests', () => {
23
- const testDataDir = path.join(process.cwd(), 'tests/data');
24
- const cliPath = path.join(process.cwd(), 'dist/index.js');
25
-
26
- beforeEach(() => {
27
- vi.clearAllMocks();
28
- });
29
-
30
- afterEach(() => {
31
- vi.restoreAllMocks();
32
- });
33
-
34
- describe('CLI Integration', () => {
35
- it('should show help message', async () => {
36
- const { stdout } = await execAsync(`node ${cliPath} --help`);
37
-
38
- expect(stdout).toContain('AI-powered CLI tool that intelligently renames files based on their content');
39
- expect(stdout).toContain('rename [options] [directory]');
40
- expect(stdout).toContain('Commands:');
41
- });
42
-
43
- it('should show rename command help', async () => {
44
- const { stdout } = await execAsync(`node ${cliPath} rename --help`);
45
-
46
- expect(stdout).toContain('Rename files in a directory based on their content using AI analysis');
47
- expect(stdout).toContain('Arguments:');
48
- expect(stdout).toContain('directory');
49
- expect(stdout).toContain('Options:');
50
- expect(stdout).toContain('-p, --provider');
51
- expect(stdout).toContain('-k, --api-key');
52
- expect(stdout).toContain('-c, --case');
53
- expect(stdout).toContain('-t, --template');
54
- expect(stdout).toContain('-n, --name');
55
- expect(stdout).toContain('-d, --date');
56
- expect(stdout).toContain('--dry-run');
57
- expect(stdout).toContain('--max-size');
58
- });
59
-
60
- it('should show naming convention options in help', async () => {
61
- const { stdout } = await execAsync(`node ${cliPath} rename --help`);
62
-
63
- expect(stdout).toContain('kebab-case');
64
- expect(stdout).toContain('snake_case');
65
- expect(stdout).toContain('camelCase');
66
- expect(stdout).toContain('PascalCase');
67
- expect(stdout).toContain('lowercase');
68
- expect(stdout).toContain('UPPERCASE');
69
- });
70
-
71
- it('should show template category options in help', async () => {
72
- const { stdout } = await execAsync(`node ${cliPath} rename --help`);
73
-
74
- expect(stdout).toContain('document');
75
- expect(stdout).toContain('movie');
76
- expect(stdout).toContain('music');
77
- expect(stdout).toContain('series');
78
- expect(stdout).toContain('photo');
79
- expect(stdout).toContain('book');
80
- expect(stdout).toContain('general');
81
- });
82
-
83
- it('should show date format options in help', async () => {
84
- const { stdout } = await execAsync(`node ${cliPath} rename --help`);
85
-
86
- expect(stdout).toContain('YYYY-MM-DD');
87
- expect(stdout).toContain('YYYY');
88
- expect(stdout).toContain('YYYYMMDD');
89
- expect(stdout).toContain('none');
90
- });
91
-
92
- it('should show version', async () => {
93
- const { stdout } = await execAsync(`node ${cliPath} --version`);
94
-
95
- expect(stdout.trim()).toMatch(/^\d+\.\d+\.\d+$/);
96
- });
97
-
98
- it('should accept optional directory argument', async () => {
99
- const { stdout } = await execAsync(`node ${cliPath} rename --help`);
100
-
101
- // Verify directory is shown as optional (in brackets) in help
102
- expect(stdout).toContain('[directory]');
103
- expect(stdout).toContain('current directory');
104
- expect(stdout).toContain('(default: ".")');
105
- });
106
-
107
- it('should handle non-existent directory', async () => {
108
- try {
109
- await execAsync(`node ${cliPath} rename /non/existent/directory --dry-run`, {
110
- input: 'test-key\n'
111
- });
112
- expect.fail('Should have thrown an error');
113
- } catch (error: any) {
114
- expect(error.stderr || error.stdout).toContain('Error:');
115
- }
116
- });
117
- });
118
-
119
- describe('Full Workflow Integration', () => {
120
- it('should process files with mock AI service (dry run)', async () => {
121
- // This test would need actual AI service mocking at the CLI level
122
- // For now, we'll test the structure
123
-
124
- const testDir = path.join(testDataDir);
125
-
126
- // Mock the AI service response by setting environment variables or config
127
- process.env.MOCK_AI_RESPONSE = 'test-document-name';
128
-
129
- try {
130
- // This would fail without real API key, but tests the flow
131
- const command = `echo "test-key" | node ${cliPath} rename ${testDir} --dry-run --provider claude`;
132
-
133
- // For a real test, we'd need to mock the AI service at a higher level
134
- // This is a placeholder for the integration test structure
135
- expect(true).toBe(true); // Placeholder assertion
136
- } catch (error) {
137
- // Expected in test environment without real API key
138
- expect(true).toBe(true);
139
- } finally {
140
- delete process.env.MOCK_AI_RESPONSE;
141
- }
142
- });
143
- });
144
-
145
- describe('File Processing Integration', () => {
146
- it('should detect supported files correctly', async () => {
147
- // Create a temporary test directory structure
148
- const tempDir = path.join(process.cwd(), 'temp-test');
149
-
150
- try {
151
- await fs.mkdir(tempDir, { recursive: true });
152
- await fs.writeFile(path.join(tempDir, 'test.txt'), 'Test content');
153
- await fs.writeFile(path.join(tempDir, 'test.md'), '# Test markdown');
154
- await fs.writeFile(path.join(tempDir, 'unsupported.xyz'), 'Unsupported file');
155
-
156
- // The actual CLI would process only supported files
157
- // This test validates the file detection logic
158
-
159
- const files = await fs.readdir(tempDir);
160
- const supportedExtensions = ['.txt', '.md', '.pdf', '.docx', '.xlsx'];
161
- const supportedFiles = files.filter(file =>
162
- supportedExtensions.some(ext => file.endsWith(ext))
163
- );
164
-
165
- expect(supportedFiles).toHaveLength(2);
166
- expect(supportedFiles).toContain('test.txt');
167
- expect(supportedFiles).toContain('test.md');
168
- expect(supportedFiles).not.toContain('unsupported.xyz');
169
-
170
- } finally {
171
- // Clean up
172
- try {
173
- await fs.rm(tempDir, { recursive: true, force: true });
174
- } catch (error) {
175
- // Ignore cleanup errors
176
- }
177
- }
178
- });
179
- });
180
-
181
- describe('Error Handling Integration', () => {
182
- it('should handle parser errors gracefully', async () => {
183
- // Test that the application handles various error conditions
184
- // without crashing and provides meaningful error messages
185
-
186
- const invalidFiles = [
187
- 'non-existent.pdf',
188
- 'empty.txt',
189
- 'corrupted.docx'
190
- ];
191
-
192
- // Each of these should be handled gracefully by the application
193
- // without causing the entire process to fail
194
-
195
- expect(invalidFiles.length).toBeGreaterThan(0); // Placeholder assertion
196
- });
197
-
198
- it('should validate configuration parameters', async () => {
199
- const invalidConfigs = [
200
- { maxSize: -1 },
201
- { maxSize: 'invalid' },
202
- { provider: 'invalid-provider' }
203
- ];
204
-
205
- // Each invalid config should be caught and handled appropriately
206
- expect(invalidConfigs.length).toBeGreaterThan(0); // Placeholder assertion
207
- });
208
- });
209
- });