spck 0.3.1
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.
- package/.oxlintrc.json +49 -0
- package/LICENSE +21 -0
- package/README.md +631 -0
- package/bin/cli.js +20 -0
- package/bin/validate-cwd.js +41 -0
- package/dist/config/__tests__/config.test.d.ts +2 -0
- package/dist/config/__tests__/config.test.js +262 -0
- package/dist/config/__tests__/credentials.test.d.ts +2 -0
- package/dist/config/__tests__/credentials.test.js +360 -0
- package/dist/config/config.d.ts +33 -0
- package/dist/config/config.js +185 -0
- package/dist/config/credentials.d.ts +75 -0
- package/dist/config/credentials.js +259 -0
- package/dist/config/server-selection.d.ts +40 -0
- package/dist/config/server-selection.js +130 -0
- package/dist/connection/__tests__/firebase-auth.test.d.ts +2 -0
- package/dist/connection/__tests__/firebase-auth.test.js +96 -0
- package/dist/connection/__tests__/hmac.test.d.ts +2 -0
- package/dist/connection/__tests__/hmac.test.js +372 -0
- package/dist/connection/auth.d.ts +13 -0
- package/dist/connection/auth.js +91 -0
- package/dist/connection/firebase-auth.d.ts +40 -0
- package/dist/connection/firebase-auth.js +429 -0
- package/dist/connection/hmac.d.ts +24 -0
- package/dist/connection/hmac.js +109 -0
- package/dist/i18n/index.d.ts +25 -0
- package/dist/i18n/index.js +101 -0
- package/dist/i18n/locales/en.json +313 -0
- package/dist/i18n/locales/es.json +302 -0
- package/dist/i18n/locales/fr.json +302 -0
- package/dist/i18n/locales/id.json +302 -0
- package/dist/i18n/locales/ja.json +302 -0
- package/dist/i18n/locales/ko.json +302 -0
- package/dist/i18n/locales/locales/en.json +309 -0
- package/dist/i18n/locales/locales/es.json +302 -0
- package/dist/i18n/locales/locales/fr.json +302 -0
- package/dist/i18n/locales/locales/id.json +302 -0
- package/dist/i18n/locales/locales/ja.json +302 -0
- package/dist/i18n/locales/locales/ko.json +302 -0
- package/dist/i18n/locales/locales/pt.json +302 -0
- package/dist/i18n/locales/locales/zh-Hans.json +302 -0
- package/dist/i18n/locales/pt.json +302 -0
- package/dist/i18n/locales/zh-Hans.json +302 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +493 -0
- package/dist/proxy/ProxyClient.d.ts +125 -0
- package/dist/proxy/ProxyClient.js +781 -0
- package/dist/proxy/ProxySocketWrapper.d.ts +43 -0
- package/dist/proxy/ProxySocketWrapper.js +98 -0
- package/dist/proxy/__tests__/ProxyClient.test.d.ts +2 -0
- package/dist/proxy/__tests__/ProxyClient.test.js +445 -0
- package/dist/proxy/__tests__/ProxySocketWrapper.test.d.ts +2 -0
- package/dist/proxy/__tests__/ProxySocketWrapper.test.js +190 -0
- package/dist/proxy/__tests__/handshake-validation.test.d.ts +2 -0
- package/dist/proxy/__tests__/handshake-validation.test.js +282 -0
- package/dist/proxy/__tests__/token-refresh-race.test.d.ts +14 -0
- package/dist/proxy/__tests__/token-refresh-race.test.js +173 -0
- package/dist/proxy/chunking.d.ts +53 -0
- package/dist/proxy/chunking.js +127 -0
- package/dist/proxy/handshake-validation.d.ts +21 -0
- package/dist/proxy/handshake-validation.js +49 -0
- package/dist/rpc/__tests__/router.test.d.ts +2 -0
- package/dist/rpc/__tests__/router.test.js +262 -0
- package/dist/rpc/router.d.ts +37 -0
- package/dist/rpc/router.js +132 -0
- package/dist/services/BrowserProxyService.d.ts +13 -0
- package/dist/services/BrowserProxyService.js +139 -0
- package/dist/services/FilesystemService.d.ts +99 -0
- package/dist/services/FilesystemService.js +742 -0
- package/dist/services/GitService.d.ts +243 -0
- package/dist/services/GitService.js +1439 -0
- package/dist/services/SearchService.d.ts +93 -0
- package/dist/services/SearchService.js +670 -0
- package/dist/services/TerminalService.d.ts +62 -0
- package/dist/services/TerminalService.js +337 -0
- package/dist/services/__tests__/BrowserProxyService.test.d.ts +2 -0
- package/dist/services/__tests__/BrowserProxyService.test.js +145 -0
- package/dist/services/__tests__/FilesystemService.test.d.ts +2 -0
- package/dist/services/__tests__/FilesystemService.test.js +609 -0
- package/dist/services/__tests__/GitService.test.d.ts +2 -0
- package/dist/services/__tests__/GitService.test.js +953 -0
- package/dist/services/__tests__/SearchService.test.d.ts +2 -0
- package/dist/services/__tests__/SearchService.test.js +384 -0
- package/dist/services/__tests__/TerminalService.test.d.ts +2 -0
- package/dist/services/__tests__/TerminalService.test.js +513 -0
- package/dist/setup/wizard.d.ts +10 -0
- package/dist/setup/wizard.js +172 -0
- package/dist/types.d.ts +196 -0
- package/dist/types.js +44 -0
- package/dist/utils/__tests__/gitignore.test.d.ts +2 -0
- package/dist/utils/__tests__/gitignore.test.js +127 -0
- package/dist/utils/gitignore.d.ts +24 -0
- package/dist/utils/gitignore.js +77 -0
- package/dist/utils/logger.d.ts +96 -0
- package/dist/utils/logger.js +456 -0
- package/dist/utils/project-dir.d.ts +51 -0
- package/dist/utils/project-dir.js +191 -0
- package/dist/utils/ripgrep.d.ts +34 -0
- package/dist/utils/ripgrep.js +148 -0
- package/dist/utils/tool-detection.d.ts +17 -0
- package/dist/utils/tool-detection.js +126 -0
- package/dist/watcher/FileWatcher.d.ts +10 -0
- package/dist/watcher/FileWatcher.js +42 -0
- package/package.json +70 -0
- package/src/config/__tests__/config.test.ts +318 -0
- package/src/config/__tests__/credentials.test.ts +494 -0
- package/src/config/config.ts +206 -0
- package/src/config/credentials.ts +302 -0
- package/src/config/server-selection.ts +150 -0
- package/src/connection/__tests__/firebase-auth.test.ts +121 -0
- package/src/connection/__tests__/hmac.test.ts +509 -0
- package/src/connection/auth.ts +140 -0
- package/src/connection/firebase-auth.ts +504 -0
- package/src/connection/hmac.ts +139 -0
- package/src/i18n/index.ts +119 -0
- package/src/i18n/locales/en.json +313 -0
- package/src/i18n/locales/es.json +302 -0
- package/src/i18n/locales/fr.json +302 -0
- package/src/i18n/locales/id.json +302 -0
- package/src/i18n/locales/ja.json +302 -0
- package/src/i18n/locales/ko.json +302 -0
- package/src/i18n/locales/pt.json +302 -0
- package/src/i18n/locales/zh-Hans.json +302 -0
- package/src/index.ts +542 -0
- package/src/proxy/ProxyClient.ts +968 -0
- package/src/proxy/ProxySocketWrapper.ts +113 -0
- package/src/proxy/__tests__/ProxyClient.test.ts +575 -0
- package/src/proxy/__tests__/ProxySocketWrapper.test.ts +251 -0
- package/src/proxy/__tests__/handshake-validation.test.ts +367 -0
- package/src/proxy/chunking.ts +162 -0
- package/src/proxy/handshake-validation.ts +64 -0
- package/src/rpc/__tests__/router.test.ts +400 -0
- package/src/rpc/router.ts +183 -0
- package/src/services/BrowserProxyService.ts +179 -0
- package/src/services/FilesystemService.ts +841 -0
- package/src/services/GitService.ts +1639 -0
- package/src/services/SearchService.ts +809 -0
- package/src/services/TerminalService.ts +413 -0
- package/src/services/__tests__/BrowserProxyService.test.ts +155 -0
- package/src/services/__tests__/FilesystemService.test.ts +1002 -0
- package/src/services/__tests__/GitService.test.ts +1552 -0
- package/src/services/__tests__/SearchService.test.ts +484 -0
- package/src/services/__tests__/TerminalService.test.ts +702 -0
- package/src/setup/wizard.ts +242 -0
- package/src/types/fossil-delta.d.ts +4 -0
- package/src/types.ts +287 -0
- package/src/utils/__tests__/gitignore.test.ts +174 -0
- package/src/utils/gitignore.ts +91 -0
- package/src/utils/logger.ts +578 -0
- package/src/utils/project-dir.ts +218 -0
- package/src/utils/ripgrep.ts +180 -0
- package/src/utils/tool-detection.ts +141 -0
- package/src/watcher/FileWatcher.ts +53 -0
- package/tsconfig.json +24 -0
- package/vitest.config.ts +19 -0
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
2
|
+
/**
|
|
3
|
+
* Tests for SearchService - Line Trimming Functionality
|
|
4
|
+
*/
|
|
5
|
+
import { SearchService } from '../SearchService.js';
|
|
6
|
+
// Mock ripgrep utilities to prevent spawning child processes during tests
|
|
7
|
+
vi.mock('../../utils/ripgrep', () => ({
|
|
8
|
+
isRipgrepAvailable: vi.fn().mockResolvedValue(false),
|
|
9
|
+
executeRipgrep: vi.fn(),
|
|
10
|
+
executeRipgrepStream: vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
describe('SearchService - Line Trimming', () => {
|
|
13
|
+
let service;
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
service = new SearchService(process.cwd(), 10 * 1024 * 1024, 64 * 1024);
|
|
16
|
+
});
|
|
17
|
+
// Helper to access private trimLineToMatch method
|
|
18
|
+
function trimLineToMatch(lineText, matchStart, matchEnd, maxLength) {
|
|
19
|
+
return service.trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
20
|
+
}
|
|
21
|
+
describe('trimLineToMatch', () => {
|
|
22
|
+
it('should return full line if shorter than maxLength', () => {
|
|
23
|
+
const lineText = 'Short line with test';
|
|
24
|
+
const result = trimLineToMatch(lineText, 16, 20, 100);
|
|
25
|
+
expect(result.line).toBe(lineText);
|
|
26
|
+
expect(result.offset).toBe(0);
|
|
27
|
+
});
|
|
28
|
+
it('should trim long line and center match', () => {
|
|
29
|
+
const lineText = 'This is a very long line with the word test in the middle and more content after';
|
|
30
|
+
const matchStart = lineText.indexOf('test'); // position of "test" = 39
|
|
31
|
+
const matchEnd = matchStart + 4; // 43
|
|
32
|
+
const maxLength = 40;
|
|
33
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
34
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
35
|
+
expect(result.line).toContain('test');
|
|
36
|
+
// Verify match is in the trimmed line
|
|
37
|
+
const relativeStart = matchStart - result.offset;
|
|
38
|
+
const relativeEnd = matchEnd - result.offset;
|
|
39
|
+
expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
|
|
40
|
+
});
|
|
41
|
+
it('should handle match at start of line', () => {
|
|
42
|
+
const lineText = 'test is at the start of this very long line with lots of content';
|
|
43
|
+
const matchStart = 0;
|
|
44
|
+
const matchEnd = 4;
|
|
45
|
+
const maxLength = 30;
|
|
46
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
47
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
48
|
+
expect(result.line).toMatch(/^test/);
|
|
49
|
+
expect(result.offset).toBe(0);
|
|
50
|
+
});
|
|
51
|
+
it('should handle match at end of line', () => {
|
|
52
|
+
const lineText = 'This is a very long line with lots of content and the match is at the end test';
|
|
53
|
+
const matchStart = lineText.indexOf('test'); // position of "test" = 75
|
|
54
|
+
const matchEnd = matchStart + 4; // 79
|
|
55
|
+
const maxLength = 30;
|
|
56
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
57
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
58
|
+
expect(result.line).toMatch(/test$/);
|
|
59
|
+
// Verify offset is calculated correctly
|
|
60
|
+
const relativeStart = matchStart - result.offset;
|
|
61
|
+
expect(result.line.substring(relativeStart)).toBe('test');
|
|
62
|
+
});
|
|
63
|
+
it('should handle match longer than maxLength', () => {
|
|
64
|
+
const lineText = 'Before verylongmatchthatexceedsmaxlength After';
|
|
65
|
+
const matchStart = 7;
|
|
66
|
+
const matchEnd = 40;
|
|
67
|
+
const maxLength = 20;
|
|
68
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
69
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
70
|
+
expect(result.offset).toBe(matchStart);
|
|
71
|
+
expect(result.line).toMatch(/^verylongmatch/);
|
|
72
|
+
});
|
|
73
|
+
it('should handle maxLength of 38 (actual UI value)', () => {
|
|
74
|
+
const lineText = 'Some code before the test keyword and some code after it';
|
|
75
|
+
const matchStart = 21; // position of "test"
|
|
76
|
+
const matchEnd = 25;
|
|
77
|
+
const maxLength = 38;
|
|
78
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
79
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
80
|
+
expect(result.line).toContain('test');
|
|
81
|
+
// Verify match positions are correct
|
|
82
|
+
const relativeStart = matchStart - result.offset;
|
|
83
|
+
const relativeEnd = matchEnd - result.offset;
|
|
84
|
+
expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
|
|
85
|
+
});
|
|
86
|
+
it('should handle invalid maxLength (0 or negative)', () => {
|
|
87
|
+
const lineText = 'Line with test';
|
|
88
|
+
const result = trimLineToMatch(lineText, 10, 14, 0);
|
|
89
|
+
// Should use default maxLength of 500
|
|
90
|
+
expect(result.line).toBe(lineText);
|
|
91
|
+
expect(result.offset).toBe(0);
|
|
92
|
+
});
|
|
93
|
+
it('should handle invalid match positions', () => {
|
|
94
|
+
const lineText = 'Valid line text';
|
|
95
|
+
// matchStart >= matchEnd
|
|
96
|
+
const result1 = trimLineToMatch(lineText, 10, 10, 50);
|
|
97
|
+
expect(result1.line).toBe(lineText);
|
|
98
|
+
// matchStart < 0
|
|
99
|
+
const result2 = trimLineToMatch(lineText, -1, 5, 50);
|
|
100
|
+
expect(result2.line).toBe(lineText);
|
|
101
|
+
// matchEnd > lineText.length
|
|
102
|
+
const result3 = trimLineToMatch(lineText, 5, 100, 50);
|
|
103
|
+
expect(result3.line).toBe(lineText);
|
|
104
|
+
});
|
|
105
|
+
it('should provide equal context on both sides when possible', () => {
|
|
106
|
+
const lineText = 'aaaaaaaaaa test bbbbbbbbbb'; // 10 chars before and after "test"
|
|
107
|
+
const matchStart = 11;
|
|
108
|
+
const matchEnd = 15;
|
|
109
|
+
const maxLength = 20; // 4 for match + 16 for context = 8 per side
|
|
110
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
111
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
112
|
+
expect(result.line).toContain('test');
|
|
113
|
+
// Should have roughly equal context (allowing for word boundaries)
|
|
114
|
+
const relativeStart = matchStart - result.offset;
|
|
115
|
+
const beforeMatch = result.line.substring(0, relativeStart);
|
|
116
|
+
const afterMatch = result.line.substring(relativeStart + 4);
|
|
117
|
+
// Allow for some imbalance due to word boundaries and ellipsis
|
|
118
|
+
expect(Math.abs(beforeMatch.length - afterMatch.length)).toBeLessThanOrEqual(8);
|
|
119
|
+
});
|
|
120
|
+
it('should handle unicode characters correctly', () => {
|
|
121
|
+
const lineText = 'Some 中文 before test and 中文 after';
|
|
122
|
+
const matchStart = lineText.indexOf('test'); // position of "test"
|
|
123
|
+
const matchEnd = matchStart + 4;
|
|
124
|
+
const maxLength = 30;
|
|
125
|
+
const result = trimLineToMatch(lineText, matchStart, matchEnd, maxLength);
|
|
126
|
+
expect(result.line).toContain('test');
|
|
127
|
+
expect(result.line).toContain('中文');
|
|
128
|
+
const relativeStart = matchStart - result.offset;
|
|
129
|
+
const relativeEnd = matchEnd - result.offset;
|
|
130
|
+
expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
|
|
131
|
+
});
|
|
132
|
+
it('should handle very long minified line (realistic scenario)', () => {
|
|
133
|
+
// Simulate a minified JavaScript line
|
|
134
|
+
const minifiedLine = 'function(){var a=1,b=2,c=3,d=4,e=5,f=6,g=7,h=8,i=9,j=10,k=11,l=12,m=13,n=14,o=15,p=16,q=17,r=18,s=19,t=20,u=21,v=22,w=23,x=24,y=25,z=26;return console.log("test")}'.repeat(10);
|
|
135
|
+
const searchTerm = 'test';
|
|
136
|
+
const matchStart = minifiedLine.indexOf(searchTerm);
|
|
137
|
+
const matchEnd = matchStart + searchTerm.length;
|
|
138
|
+
const maxLength = 38;
|
|
139
|
+
const result = trimLineToMatch(minifiedLine, matchStart, matchEnd, maxLength);
|
|
140
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
141
|
+
expect(result.line).toContain('test');
|
|
142
|
+
expect(result.line.length).toBeGreaterThan(0);
|
|
143
|
+
// Verify the match is correctly positioned
|
|
144
|
+
const relativeStart = matchStart - result.offset;
|
|
145
|
+
const relativeEnd = matchEnd - result.offset;
|
|
146
|
+
expect(result.line.substring(relativeStart, relativeEnd)).toBe('test');
|
|
147
|
+
});
|
|
148
|
+
it('should handle match at exact maxLength boundary', () => {
|
|
149
|
+
const lineText = 'x'.repeat(100);
|
|
150
|
+
const modifiedLine = lineText.substring(0, 50) + 'test' + lineText.substring(54);
|
|
151
|
+
const matchStart = 50;
|
|
152
|
+
const matchEnd = 54;
|
|
153
|
+
const maxLength = 54; // Exactly match + all before
|
|
154
|
+
const result = trimLineToMatch(modifiedLine, matchStart, matchEnd, maxLength);
|
|
155
|
+
expect(result.line).toContain('test');
|
|
156
|
+
expect(result.line.length).toBeLessThanOrEqual(maxLength);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('SearchService - Search Functionality', () => {
|
|
161
|
+
let service;
|
|
162
|
+
let testRoot;
|
|
163
|
+
let mockSocket;
|
|
164
|
+
beforeEach(async () => {
|
|
165
|
+
const fs = require('fs/promises');
|
|
166
|
+
const path = require('path');
|
|
167
|
+
const os = require('os');
|
|
168
|
+
// Create temporary test directory
|
|
169
|
+
testRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'search-test-'));
|
|
170
|
+
service = new SearchService(testRoot, 10 * 1024 * 1024, 64 * 1024);
|
|
171
|
+
// Create mock socket for RPC notifications
|
|
172
|
+
mockSocket = {
|
|
173
|
+
id: 'test-socket',
|
|
174
|
+
data: { uid: 'test-user', deviceId: 'test-device' },
|
|
175
|
+
emit: vi.fn(),
|
|
176
|
+
on: vi.fn(),
|
|
177
|
+
off: vi.fn(),
|
|
178
|
+
};
|
|
179
|
+
// Create test files
|
|
180
|
+
await fs.writeFile(path.join(testRoot, 'file1.txt'), 'Hello world\nThis is a test\nAnother line');
|
|
181
|
+
await fs.writeFile(path.join(testRoot, 'file2.txt'), 'Testing search\nNo matches here');
|
|
182
|
+
await fs.writeFile(path.join(testRoot, 'file3.js'), 'const test = 42;\nconsole.log(test);');
|
|
183
|
+
// Create subdirectory with files
|
|
184
|
+
await fs.mkdir(path.join(testRoot, 'subdir'));
|
|
185
|
+
await fs.writeFile(path.join(testRoot, 'subdir', 'nested.txt'), 'Nested test file\nWith multiple lines');
|
|
186
|
+
});
|
|
187
|
+
afterEach(async () => {
|
|
188
|
+
const fs = require('fs/promises');
|
|
189
|
+
// Clean up test directory
|
|
190
|
+
try {
|
|
191
|
+
await fs.rm(testRoot, { recursive: true, force: true });
|
|
192
|
+
}
|
|
193
|
+
catch { }
|
|
194
|
+
});
|
|
195
|
+
describe('handle method', () => {
|
|
196
|
+
it('should handle findWithStream method', async () => {
|
|
197
|
+
await expect(service.handle('findWithStream', {
|
|
198
|
+
glob: '**/*.txt',
|
|
199
|
+
maxResults: 10,
|
|
200
|
+
maxLength: 100,
|
|
201
|
+
searchTerm: 'test',
|
|
202
|
+
matchCase: false,
|
|
203
|
+
useRegEx: false,
|
|
204
|
+
onlyWholeWords: false,
|
|
205
|
+
}, mockSocket)).resolves.not.toThrow();
|
|
206
|
+
// Should have sent results to socket
|
|
207
|
+
expect(mockSocket.emit).toHaveBeenCalled();
|
|
208
|
+
});
|
|
209
|
+
it('should throw error for unknown method', async () => {
|
|
210
|
+
await expect(service.handle('unknownMethod', {}, mockSocket)).rejects.toMatchObject({
|
|
211
|
+
code: -32601, // METHOD_NOT_FOUND
|
|
212
|
+
message: expect.stringContaining('Method not found'),
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
describe('findWithStream (Node.js implementation)', () => {
|
|
217
|
+
it('should find matches in text files', async () => {
|
|
218
|
+
const results = [];
|
|
219
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
220
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
221
|
+
results.push(...data.params.results);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
await service.handle('findWithStream', {
|
|
225
|
+
glob: '*.txt', // Match .txt files in root
|
|
226
|
+
maxResults: 100,
|
|
227
|
+
maxLength: 100,
|
|
228
|
+
searchTerm: 'test',
|
|
229
|
+
matchCase: false,
|
|
230
|
+
useRegEx: false,
|
|
231
|
+
onlyWholeWords: false,
|
|
232
|
+
}, mockSocket);
|
|
233
|
+
// Should find matches in file1.txt
|
|
234
|
+
expect(results.length).toBeGreaterThan(0);
|
|
235
|
+
expect(results.some((r) => r.path.includes('file1.txt'))).toBe(true);
|
|
236
|
+
});
|
|
237
|
+
it('should respect case sensitivity', async () => {
|
|
238
|
+
const results = [];
|
|
239
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
240
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
241
|
+
results.push(...data.params.results);
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
await service.handle('findWithStream', {
|
|
245
|
+
glob: '**/*.txt',
|
|
246
|
+
maxResults: 100,
|
|
247
|
+
maxLength: 100,
|
|
248
|
+
searchTerm: 'TEST',
|
|
249
|
+
matchCase: true,
|
|
250
|
+
useRegEx: false,
|
|
251
|
+
onlyWholeWords: false,
|
|
252
|
+
}, mockSocket);
|
|
253
|
+
// Should not find matches (all lowercase in files)
|
|
254
|
+
expect(results.length).toBe(0);
|
|
255
|
+
});
|
|
256
|
+
it('should support regex patterns', async () => {
|
|
257
|
+
const results = [];
|
|
258
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
259
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
260
|
+
results.push(...data.params.results);
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
await service.handle('findWithStream', {
|
|
264
|
+
glob: '**/*.txt',
|
|
265
|
+
maxResults: 100,
|
|
266
|
+
maxLength: 100,
|
|
267
|
+
searchTerm: 't[eE]st',
|
|
268
|
+
matchCase: false,
|
|
269
|
+
useRegEx: true,
|
|
270
|
+
onlyWholeWords: false,
|
|
271
|
+
}, mockSocket);
|
|
272
|
+
expect(results.length).toBeGreaterThan(0);
|
|
273
|
+
});
|
|
274
|
+
it('should respect whole word matching', async () => {
|
|
275
|
+
const results = [];
|
|
276
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
277
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
278
|
+
results.push(...data.params.results);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
await service.handle('findWithStream', {
|
|
282
|
+
glob: '**/*.txt',
|
|
283
|
+
maxResults: 100,
|
|
284
|
+
maxLength: 100,
|
|
285
|
+
searchTerm: 'test',
|
|
286
|
+
matchCase: false,
|
|
287
|
+
useRegEx: false,
|
|
288
|
+
onlyWholeWords: true,
|
|
289
|
+
}, mockSocket);
|
|
290
|
+
// Should find whole word "test" but not "Testing"
|
|
291
|
+
const matchValues = results.map((r) => r.value);
|
|
292
|
+
expect(matchValues).toContain('test');
|
|
293
|
+
expect(matchValues.some((v) => v.toLowerCase() === 'testing')).toBe(false);
|
|
294
|
+
});
|
|
295
|
+
it('should respect maxResults limit', async () => {
|
|
296
|
+
const results = [];
|
|
297
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
298
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
299
|
+
results.push(...data.params.results);
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
await service.handle('findWithStream', {
|
|
303
|
+
glob: '**/*',
|
|
304
|
+
maxResults: 2,
|
|
305
|
+
maxLength: 100,
|
|
306
|
+
searchTerm: 'test',
|
|
307
|
+
matchCase: false,
|
|
308
|
+
useRegEx: false,
|
|
309
|
+
onlyWholeWords: false,
|
|
310
|
+
}, mockSocket);
|
|
311
|
+
expect(results.length).toBeLessThanOrEqual(2);
|
|
312
|
+
});
|
|
313
|
+
it('should filter by glob pattern', async () => {
|
|
314
|
+
const results = [];
|
|
315
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
316
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
317
|
+
results.push(...data.params.results);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
await service.handle('findWithStream', {
|
|
321
|
+
glob: '**/*.js',
|
|
322
|
+
maxResults: 100,
|
|
323
|
+
maxLength: 100,
|
|
324
|
+
searchTerm: 'test',
|
|
325
|
+
matchCase: false,
|
|
326
|
+
useRegEx: false,
|
|
327
|
+
onlyWholeWords: false,
|
|
328
|
+
}, mockSocket);
|
|
329
|
+
// Should only find matches in .js files
|
|
330
|
+
expect(results.every((r) => r.path.endsWith('.js'))).toBe(true);
|
|
331
|
+
});
|
|
332
|
+
it('should send completion notification', async () => {
|
|
333
|
+
await service.handle('findWithStream', {
|
|
334
|
+
glob: '**/*.txt',
|
|
335
|
+
maxResults: 100,
|
|
336
|
+
maxLength: 100,
|
|
337
|
+
searchTerm: 'test',
|
|
338
|
+
matchCase: false,
|
|
339
|
+
useRegEx: false,
|
|
340
|
+
onlyWholeWords: false,
|
|
341
|
+
}, mockSocket);
|
|
342
|
+
// Find the done notification
|
|
343
|
+
const doneCall = mockSocket.emit.mock.calls.find((call) => call[0] === 'rpc' &&
|
|
344
|
+
call[1].method === 'search.results' &&
|
|
345
|
+
call[1].params.done === true);
|
|
346
|
+
expect(doneCall).toBeDefined();
|
|
347
|
+
expect(doneCall[1].params.total).toBeGreaterThanOrEqual(0);
|
|
348
|
+
});
|
|
349
|
+
it('should include correct match positions', async () => {
|
|
350
|
+
const results = [];
|
|
351
|
+
mockSocket.emit.mockImplementation((event, data) => {
|
|
352
|
+
if (event === 'rpc' && data.method === 'search.results') {
|
|
353
|
+
results.push(...data.params.results);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
await service.handle('findWithStream', {
|
|
357
|
+
glob: 'file1.txt', // Match specific file
|
|
358
|
+
maxResults: 100,
|
|
359
|
+
maxLength: 100,
|
|
360
|
+
searchTerm: 'test',
|
|
361
|
+
matchCase: false,
|
|
362
|
+
useRegEx: false,
|
|
363
|
+
onlyWholeWords: false,
|
|
364
|
+
}, mockSocket);
|
|
365
|
+
expect(results.length).toBeGreaterThan(0);
|
|
366
|
+
const match = results[0];
|
|
367
|
+
// Verify match structure
|
|
368
|
+
expect(match).toHaveProperty('start');
|
|
369
|
+
expect(match).toHaveProperty('end');
|
|
370
|
+
expect(match).toHaveProperty('line');
|
|
371
|
+
expect(match).toHaveProperty('value');
|
|
372
|
+
expect(match).toHaveProperty('match');
|
|
373
|
+
expect(match).toHaveProperty('path');
|
|
374
|
+
// Verify value is extracted correctly from line
|
|
375
|
+
expect(match.line.substring(match.match.start, match.match.end)).toBe(match.value);
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
describe('cleanup', () => {
|
|
379
|
+
it('should cleanup without errors', () => {
|
|
380
|
+
expect(() => service.cleanup()).not.toThrow();
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
//# sourceMappingURL=SearchService.test.js.map
|