codeep 1.2.17 → 1.2.18

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 (62) hide show
  1. package/README.md +20 -7
  2. package/dist/api/index.d.ts +7 -0
  3. package/dist/api/index.js +21 -17
  4. package/dist/renderer/App.d.ts +1 -5
  5. package/dist/renderer/App.js +106 -486
  6. package/dist/renderer/agentExecution.d.ts +36 -0
  7. package/dist/renderer/agentExecution.js +394 -0
  8. package/dist/renderer/commands.d.ts +16 -0
  9. package/dist/renderer/commands.js +838 -0
  10. package/dist/renderer/handlers.d.ts +87 -0
  11. package/dist/renderer/handlers.js +260 -0
  12. package/dist/renderer/highlight.d.ts +18 -0
  13. package/dist/renderer/highlight.js +130 -0
  14. package/dist/renderer/main.d.ts +4 -2
  15. package/dist/renderer/main.js +103 -1550
  16. package/dist/utils/agent.d.ts +5 -15
  17. package/dist/utils/agent.js +9 -693
  18. package/dist/utils/agentChat.d.ts +46 -0
  19. package/dist/utils/agentChat.js +343 -0
  20. package/dist/utils/agentStream.d.ts +23 -0
  21. package/dist/utils/agentStream.js +216 -0
  22. package/dist/utils/keychain.js +3 -2
  23. package/dist/utils/learning.js +9 -3
  24. package/dist/utils/mcpIntegration.d.ts +61 -0
  25. package/dist/utils/mcpIntegration.js +154 -0
  26. package/dist/utils/project.js +8 -3
  27. package/dist/utils/skills.js +21 -11
  28. package/dist/utils/smartContext.d.ts +4 -0
  29. package/dist/utils/smartContext.js +51 -14
  30. package/dist/utils/toolExecution.d.ts +27 -0
  31. package/dist/utils/toolExecution.js +525 -0
  32. package/dist/utils/toolParsing.d.ts +18 -0
  33. package/dist/utils/toolParsing.js +302 -0
  34. package/dist/utils/tools.d.ts +11 -24
  35. package/dist/utils/tools.js +22 -1187
  36. package/package.json +3 -1
  37. package/dist/config/config.test.d.ts +0 -1
  38. package/dist/config/config.test.js +0 -157
  39. package/dist/config/providers.test.d.ts +0 -1
  40. package/dist/config/providers.test.js +0 -187
  41. package/dist/hooks/index.d.ts +0 -4
  42. package/dist/hooks/index.js +0 -4
  43. package/dist/hooks/useAgent.d.ts +0 -29
  44. package/dist/hooks/useAgent.js +0 -148
  45. package/dist/utils/agent.test.d.ts +0 -1
  46. package/dist/utils/agent.test.js +0 -315
  47. package/dist/utils/git.test.d.ts +0 -1
  48. package/dist/utils/git.test.js +0 -193
  49. package/dist/utils/gitignore.test.d.ts +0 -1
  50. package/dist/utils/gitignore.test.js +0 -167
  51. package/dist/utils/project.test.d.ts +0 -1
  52. package/dist/utils/project.test.js +0 -212
  53. package/dist/utils/ratelimit.test.d.ts +0 -1
  54. package/dist/utils/ratelimit.test.js +0 -131
  55. package/dist/utils/retry.test.d.ts +0 -1
  56. package/dist/utils/retry.test.js +0 -163
  57. package/dist/utils/smartContext.test.d.ts +0 -1
  58. package/dist/utils/smartContext.test.js +0 -382
  59. package/dist/utils/tools.test.d.ts +0 -1
  60. package/dist/utils/tools.test.js +0 -681
  61. package/dist/utils/validation.test.d.ts +0 -1
  62. package/dist/utils/validation.test.js +0 -164
@@ -1,167 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- // Mock fs
3
- vi.mock('fs', () => ({
4
- existsSync: vi.fn(),
5
- readFileSync: vi.fn(),
6
- }));
7
- import { existsSync, readFileSync } from 'fs';
8
- import { loadIgnoreRules, isIgnored } from './gitignore.js';
9
- const mockExistsSync = existsSync;
10
- const mockReadFileSync = readFileSync;
11
- beforeEach(() => {
12
- vi.clearAllMocks();
13
- });
14
- describe('loadIgnoreRules', () => {
15
- it('returns built-in ignores when no .gitignore exists', () => {
16
- mockExistsSync.mockReturnValue(false);
17
- const rules = loadIgnoreRules('/project');
18
- expect(rules.projectRoot).toBe('/project');
19
- expect(rules.patterns.length).toBeGreaterThan(0);
20
- // node_modules should be a built-in
21
- expect(isIgnored('node_modules/foo.js', rules)).toBe(true);
22
- });
23
- it('loads .gitignore when it exists', () => {
24
- mockExistsSync.mockReturnValue(true);
25
- mockReadFileSync.mockReturnValue('*.log\ntmp/\n');
26
- const rules = loadIgnoreRules('/project');
27
- expect(isIgnored('error.log', rules)).toBe(true);
28
- expect(isIgnored('tmp/cache', rules)).toBe(true);
29
- });
30
- it('handles read errors gracefully', () => {
31
- mockExistsSync.mockReturnValue(true);
32
- mockReadFileSync.mockImplementation(() => { throw new Error('EACCES'); });
33
- const rules = loadIgnoreRules('/project');
34
- // Should still have built-in ignores
35
- expect(rules.patterns.length).toBeGreaterThan(0);
36
- });
37
- });
38
- describe('isIgnored — built-in ignores', () => {
39
- let rules;
40
- beforeEach(() => {
41
- mockExistsSync.mockReturnValue(false);
42
- rules = loadIgnoreRules('/project');
43
- });
44
- it('ignores node_modules', () => {
45
- expect(isIgnored('node_modules/package/index.js', rules)).toBe(true);
46
- expect(isIgnored('/project/node_modules', rules)).toBe(true);
47
- });
48
- it('ignores .git', () => {
49
- expect(isIgnored('.git/config', rules)).toBe(true);
50
- });
51
- it('ignores .codeep', () => {
52
- expect(isIgnored('.codeep/state.json', rules)).toBe(true);
53
- });
54
- it('ignores dist and build', () => {
55
- expect(isIgnored('dist/bundle.js', rules)).toBe(true);
56
- expect(isIgnored('build/output.js', rules)).toBe(true);
57
- });
58
- it('ignores __pycache__', () => {
59
- expect(isIgnored('src/__pycache__/module.pyc', rules)).toBe(true);
60
- });
61
- it('ignores .next', () => {
62
- expect(isIgnored('.next/static/chunks/main.js', rules)).toBe(true);
63
- });
64
- it('ignores coverage', () => {
65
- expect(isIgnored('coverage/lcov.info', rules)).toBe(true);
66
- });
67
- it('does not ignore regular source files', () => {
68
- expect(isIgnored('src/index.ts', rules)).toBe(false);
69
- expect(isIgnored('package.json', rules)).toBe(false);
70
- expect(isIgnored('README.md', rules)).toBe(false);
71
- });
72
- it('does not ignore files that contain ignore names as substrings', () => {
73
- expect(isIgnored('src/builder.ts', rules)).toBe(false);
74
- expect(isIgnored('src/distribute.ts', rules)).toBe(false);
75
- });
76
- });
77
- describe('isIgnored — .gitignore patterns', () => {
78
- function rulesFrom(gitignore) {
79
- mockExistsSync.mockReturnValue(true);
80
- mockReadFileSync.mockReturnValue(gitignore);
81
- return loadIgnoreRules('/project');
82
- }
83
- it('matches simple file patterns', () => {
84
- const rules = rulesFrom('*.log');
85
- expect(isIgnored('error.log', rules)).toBe(true);
86
- expect(isIgnored('logs/debug.log', rules)).toBe(true);
87
- expect(isIgnored('error.txt', rules)).toBe(false);
88
- });
89
- it('matches directory patterns', () => {
90
- const rules = rulesFrom('tmp/');
91
- expect(isIgnored('tmp/file.txt', rules)).toBe(true);
92
- expect(isIgnored('src/tmp/file.txt', rules)).toBe(true);
93
- });
94
- it('matches anchored patterns (leading /)', () => {
95
- const rules = rulesFrom('/config.local');
96
- expect(isIgnored('config.local', rules)).toBe(true);
97
- expect(isIgnored('sub/config.local', rules)).toBe(false);
98
- });
99
- it('matches ** glob patterns', () => {
100
- const rules = rulesFrom('docs/**/*.md');
101
- expect(isIgnored('docs/readme.md', rules)).toBe(true);
102
- expect(isIgnored('docs/guides/setup.md', rules)).toBe(true);
103
- expect(isIgnored('src/readme.md', rules)).toBe(false);
104
- });
105
- it('matches ? single char wildcard', () => {
106
- const rules = rulesFrom('file?.txt');
107
- expect(isIgnored('file1.txt', rules)).toBe(true);
108
- expect(isIgnored('fileA.txt', rules)).toBe(true);
109
- expect(isIgnored('file10.txt', rules)).toBe(false);
110
- });
111
- it('matches character classes [...]', () => {
112
- const rules = rulesFrom('file[0-9].txt');
113
- expect(isIgnored('file5.txt', rules)).toBe(true);
114
- expect(isIgnored('fileA.txt', rules)).toBe(false);
115
- });
116
- it('handles negation patterns', () => {
117
- const rules = rulesFrom('*.log\n!important.log');
118
- expect(isIgnored('error.log', rules)).toBe(true);
119
- expect(isIgnored('important.log', rules)).toBe(false);
120
- });
121
- it('skips comments and empty lines', () => {
122
- const rules = rulesFrom('# this is a comment\n\n*.tmp');
123
- expect(isIgnored('file.tmp', rules)).toBe(true);
124
- expect(isIgnored('# this is a comment', rules)).toBe(false);
125
- });
126
- it('handles .env files', () => {
127
- const rules = rulesFrom('.env\n.env.local\n.env.*.local');
128
- expect(isIgnored('.env', rules)).toBe(true);
129
- expect(isIgnored('.env.local', rules)).toBe(true);
130
- expect(isIgnored('.env.production.local', rules)).toBe(true);
131
- expect(isIgnored('.env.example', rules)).toBe(false);
132
- });
133
- it('handles complex real-world .gitignore', () => {
134
- const rules = rulesFrom([
135
- 'node_modules/',
136
- '*.log',
137
- '.env',
138
- '/dist',
139
- '!dist/keep.txt',
140
- '**/*.map',
141
- ].join('\n'));
142
- expect(isIgnored('app.log', rules)).toBe(true);
143
- expect(isIgnored('.env', rules)).toBe(true);
144
- expect(isIgnored('dist/bundle.js', rules)).toBe(true);
145
- expect(isIgnored('src/utils/helper.js.map', rules)).toBe(true);
146
- expect(isIgnored('src/index.ts', rules)).toBe(false);
147
- });
148
- });
149
- describe('isIgnored — path normalization', () => {
150
- let rules;
151
- beforeEach(() => {
152
- mockExistsSync.mockReturnValue(true);
153
- mockReadFileSync.mockReturnValue('*.log');
154
- rules = loadIgnoreRules('/project');
155
- });
156
- it('handles absolute paths under project root', () => {
157
- expect(isIgnored('/project/error.log', rules)).toBe(true);
158
- expect(isIgnored('/project/src/debug.log', rules)).toBe(true);
159
- });
160
- it('handles relative paths', () => {
161
- expect(isIgnored('error.log', rules)).toBe(true);
162
- expect(isIgnored('src/debug.log', rules)).toBe(true);
163
- });
164
- it('returns false for empty path', () => {
165
- expect(isIgnored('', rules)).toBe(false);
166
- });
167
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,212 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { mkdirSync, rmSync, writeFileSync } from 'fs';
3
- import { join } from 'path';
4
- import { tmpdir } from 'os';
5
- import { isProjectDirectory, getProjectType, scanDirectory, generateTreeStructure, readProjectFile, deleteProjectFile, writeProjectFile, } from './project.js';
6
- const TEST_DIR = join(tmpdir(), 'codeep-project-test-' + Date.now());
7
- describe('project utilities', () => {
8
- beforeEach(() => {
9
- mkdirSync(TEST_DIR, { recursive: true });
10
- });
11
- afterEach(() => {
12
- try {
13
- rmSync(TEST_DIR, { recursive: true, force: true });
14
- }
15
- catch { }
16
- });
17
- describe('isProjectDirectory', () => {
18
- it('should return true for directory with package.json', () => {
19
- writeFileSync(join(TEST_DIR, 'package.json'), '{}');
20
- expect(isProjectDirectory(TEST_DIR)).toBe(true);
21
- });
22
- it('should return true for directory with Cargo.toml', () => {
23
- writeFileSync(join(TEST_DIR, 'Cargo.toml'), '[package]');
24
- expect(isProjectDirectory(TEST_DIR)).toBe(true);
25
- });
26
- it('should return true for directory with go.mod', () => {
27
- writeFileSync(join(TEST_DIR, 'go.mod'), 'module test');
28
- expect(isProjectDirectory(TEST_DIR)).toBe(true);
29
- });
30
- it('should return true for directory with .git', () => {
31
- mkdirSync(join(TEST_DIR, '.git'), { recursive: true });
32
- expect(isProjectDirectory(TEST_DIR)).toBe(true);
33
- });
34
- it('should return false for empty directory', () => {
35
- expect(isProjectDirectory(TEST_DIR)).toBe(false);
36
- });
37
- });
38
- describe('getProjectType', () => {
39
- it('should detect TypeScript project', () => {
40
- writeFileSync(join(TEST_DIR, 'package.json'), JSON.stringify({
41
- devDependencies: { typescript: '^5.0.0' }
42
- }));
43
- expect(getProjectType(TEST_DIR)).toBe('TypeScript/Node.js');
44
- });
45
- it('should detect TypeScript project with tsconfig', () => {
46
- writeFileSync(join(TEST_DIR, 'package.json'), '{}');
47
- writeFileSync(join(TEST_DIR, 'tsconfig.json'), '{}');
48
- expect(getProjectType(TEST_DIR)).toBe('TypeScript/Node.js');
49
- });
50
- it('should detect JavaScript project', () => {
51
- writeFileSync(join(TEST_DIR, 'package.json'), '{}');
52
- expect(getProjectType(TEST_DIR)).toBe('JavaScript/Node.js');
53
- });
54
- it('should detect Rust project', () => {
55
- writeFileSync(join(TEST_DIR, 'Cargo.toml'), '[package]');
56
- expect(getProjectType(TEST_DIR)).toBe('Rust');
57
- });
58
- it('should detect Go project', () => {
59
- writeFileSync(join(TEST_DIR, 'go.mod'), 'module test');
60
- expect(getProjectType(TEST_DIR)).toBe('Go');
61
- });
62
- it('should detect Python project', () => {
63
- writeFileSync(join(TEST_DIR, 'requirements.txt'), 'flask');
64
- expect(getProjectType(TEST_DIR)).toBe('Python');
65
- });
66
- it('should return Unknown for unrecognized project', () => {
67
- expect(getProjectType(TEST_DIR)).toBe('Unknown');
68
- });
69
- });
70
- describe('scanDirectory', () => {
71
- it('should scan files in directory', () => {
72
- writeFileSync(join(TEST_DIR, 'index.ts'), 'export {}');
73
- writeFileSync(join(TEST_DIR, 'utils.js'), '// utils');
74
- writeFileSync(join(TEST_DIR, 'package.json'), '{}');
75
- const files = scanDirectory(TEST_DIR);
76
- const names = files.map(f => f.name);
77
- expect(names).toContain('index.ts');
78
- expect(names).toContain('utils.js');
79
- expect(names).toContain('package.json');
80
- });
81
- it('should ignore node_modules', () => {
82
- mkdirSync(join(TEST_DIR, 'node_modules'), { recursive: true });
83
- writeFileSync(join(TEST_DIR, 'node_modules', 'dep.js'), '// dep');
84
- writeFileSync(join(TEST_DIR, 'index.ts'), 'export {}');
85
- const files = scanDirectory(TEST_DIR);
86
- const paths = files.map(f => f.relativePath);
87
- expect(paths).not.toContain('node_modules/dep.js');
88
- expect(paths.some(p => p.includes('node_modules'))).toBe(false);
89
- });
90
- it('should ignore .git directory', () => {
91
- mkdirSync(join(TEST_DIR, '.git'), { recursive: true });
92
- writeFileSync(join(TEST_DIR, '.git', 'config'), '# git config');
93
- const files = scanDirectory(TEST_DIR);
94
- const paths = files.map(f => f.relativePath);
95
- expect(paths.some(p => p.includes('.git'))).toBe(false);
96
- });
97
- it('should respect maxDepth', () => {
98
- mkdirSync(join(TEST_DIR, 'a', 'b', 'c', 'd'), { recursive: true });
99
- writeFileSync(join(TEST_DIR, 'a', 'b', 'c', 'd', 'deep.ts'), '// deep');
100
- writeFileSync(join(TEST_DIR, 'a', 'shallow.ts'), '// shallow');
101
- const files = scanDirectory(TEST_DIR, 2);
102
- const names = files.map(f => f.name);
103
- expect(names).toContain('shallow.ts');
104
- expect(names).not.toContain('deep.ts');
105
- });
106
- it('should include directories in results', () => {
107
- mkdirSync(join(TEST_DIR, 'src'), { recursive: true });
108
- writeFileSync(join(TEST_DIR, 'src', 'index.ts'), 'export {}');
109
- const files = scanDirectory(TEST_DIR);
110
- const dirs = files.filter(f => f.isDirectory);
111
- expect(dirs.some(d => d.name === 'src')).toBe(true);
112
- });
113
- });
114
- describe('generateTreeStructure', () => {
115
- it('should generate tree structure', () => {
116
- mkdirSync(join(TEST_DIR, 'src'), { recursive: true });
117
- writeFileSync(join(TEST_DIR, 'src', 'index.ts'), 'export {}');
118
- writeFileSync(join(TEST_DIR, 'package.json'), '{}');
119
- const files = scanDirectory(TEST_DIR);
120
- const tree = generateTreeStructure(files);
121
- expect(tree).toContain('src/');
122
- expect(tree).toContain('index.ts');
123
- expect(tree).toContain('package.json');
124
- });
125
- it('should truncate when exceeding maxLines', () => {
126
- // Create many files
127
- for (let i = 0; i < 50; i++) {
128
- writeFileSync(join(TEST_DIR, `file${i}.ts`), '// file');
129
- }
130
- const files = scanDirectory(TEST_DIR);
131
- const tree = generateTreeStructure(files, 10);
132
- // The function uses "(+N more)" format for truncation
133
- expect(tree).toContain('more');
134
- });
135
- });
136
- describe('readProjectFile', () => {
137
- it('should read file content', () => {
138
- const content = 'export const hello = "world";';
139
- writeFileSync(join(TEST_DIR, 'test.ts'), content);
140
- const result = readProjectFile(join(TEST_DIR, 'test.ts'));
141
- expect(result).not.toBeNull();
142
- expect(result.content).toBe(content);
143
- expect(result.truncated).toBe(false);
144
- });
145
- it('should return null for non-existent file', () => {
146
- const result = readProjectFile(join(TEST_DIR, 'nonexistent.ts'));
147
- expect(result).toBeNull();
148
- });
149
- it('should return null for directories', () => {
150
- mkdirSync(join(TEST_DIR, 'subdir'), { recursive: true });
151
- const result = readProjectFile(join(TEST_DIR, 'subdir'));
152
- expect(result).toBeNull();
153
- });
154
- it('should truncate large files', () => {
155
- // File size is 60000 bytes, maxSize is 50000
156
- // Function skips files > maxSize * 2 (100000), so 60000 should be read and truncated
157
- const largeContent = 'x'.repeat(60000);
158
- writeFileSync(join(TEST_DIR, 'large.ts'), largeContent);
159
- const result = readProjectFile(join(TEST_DIR, 'large.ts'), 50000);
160
- expect(result).not.toBeNull();
161
- expect(result.truncated).toBe(true);
162
- expect(result.content.length).toBeLessThan(largeContent.length);
163
- expect(result.content).toContain('truncated');
164
- });
165
- it('should skip very large files', () => {
166
- const hugeContent = 'x'.repeat(200000);
167
- writeFileSync(join(TEST_DIR, 'huge.ts'), hugeContent);
168
- const result = readProjectFile(join(TEST_DIR, 'huge.ts'), 50000);
169
- expect(result).toBeNull();
170
- });
171
- });
172
- describe('writeProjectFile', () => {
173
- it('should write file content', () => {
174
- const filePath = join(TEST_DIR, 'output.ts');
175
- const content = 'export const test = true;';
176
- const result = writeProjectFile(filePath, content);
177
- expect(result.success).toBe(true);
178
- const written = readProjectFile(filePath);
179
- expect(written.content).toBe(content);
180
- });
181
- it('should create parent directories', () => {
182
- const filePath = join(TEST_DIR, 'new', 'nested', 'dir', 'file.ts');
183
- const content = '// nested file';
184
- const result = writeProjectFile(filePath, content);
185
- expect(result.success).toBe(true);
186
- const written = readProjectFile(filePath);
187
- expect(written.content).toBe(content);
188
- });
189
- it('should overwrite existing file', () => {
190
- const filePath = join(TEST_DIR, 'existing.ts');
191
- writeFileSync(filePath, 'old content');
192
- const result = writeProjectFile(filePath, 'new content');
193
- expect(result.success).toBe(true);
194
- const written = readProjectFile(filePath);
195
- expect(written.content).toBe('new content');
196
- });
197
- });
198
- describe('deleteProjectFile', () => {
199
- it('should delete existing file', () => {
200
- const filePath = join(TEST_DIR, 'to-delete.ts');
201
- writeFileSync(filePath, 'delete me');
202
- const result = deleteProjectFile(filePath);
203
- expect(result.success).toBe(true);
204
- expect(readProjectFile(filePath)).toBeNull();
205
- });
206
- it('should return error for non-existent file', () => {
207
- const result = deleteProjectFile(join(TEST_DIR, 'nonexistent.ts'));
208
- expect(result.success).toBe(false);
209
- expect(result.error).toContain('does not exist');
210
- });
211
- });
212
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,131 +0,0 @@
1
- import { describe, it, expect, beforeEach, vi } from 'vitest';
2
- import { checkApiRateLimit, checkCommandRateLimit, resetRateLimits, getRateLimitStatus, } from './ratelimit.js';
3
- describe('ratelimit utilities', () => {
4
- beforeEach(() => {
5
- // Reset rate limiters before each test
6
- resetRateLimits();
7
- });
8
- describe('checkApiRateLimit', () => {
9
- it('should allow requests under limit', () => {
10
- const result = checkApiRateLimit();
11
- expect(result.allowed).toBe(true);
12
- expect(result.message).toBeUndefined();
13
- });
14
- it('should track request count', () => {
15
- // Make some requests
16
- checkApiRateLimit();
17
- checkApiRateLimit();
18
- checkApiRateLimit();
19
- const status = getRateLimitStatus();
20
- expect(status.api.count).toBe(3);
21
- });
22
- it('should block requests over limit', () => {
23
- // Make requests up to the limit (default 30)
24
- for (let i = 0; i < 30; i++) {
25
- const result = checkApiRateLimit();
26
- expect(result.allowed).toBe(true);
27
- }
28
- // Next request should be blocked
29
- const result = checkApiRateLimit();
30
- expect(result.allowed).toBe(false);
31
- expect(result.message).toContain('Rate limit exceeded');
32
- });
33
- it('should include retry message when blocked', () => {
34
- // Fill up the limit
35
- for (let i = 0; i < 30; i++) {
36
- checkApiRateLimit();
37
- }
38
- const result = checkApiRateLimit();
39
- expect(result.message).toContain('Please wait');
40
- });
41
- });
42
- describe('checkCommandRateLimit', () => {
43
- it('should allow commands under limit', () => {
44
- const result = checkCommandRateLimit();
45
- expect(result.allowed).toBe(true);
46
- });
47
- it('should track command count', () => {
48
- checkCommandRateLimit();
49
- checkCommandRateLimit();
50
- const status = getRateLimitStatus();
51
- expect(status.commands.count).toBe(2);
52
- });
53
- it('should block commands over limit', () => {
54
- // Make commands up to the limit (default 100)
55
- for (let i = 0; i < 100; i++) {
56
- checkCommandRateLimit();
57
- }
58
- const result = checkCommandRateLimit();
59
- expect(result.allowed).toBe(false);
60
- expect(result.message).toContain('Too many commands');
61
- });
62
- });
63
- describe('resetRateLimits', () => {
64
- it('should reset all counters', () => {
65
- // Make some requests
66
- checkApiRateLimit();
67
- checkApiRateLimit();
68
- checkCommandRateLimit();
69
- checkCommandRateLimit();
70
- checkCommandRateLimit();
71
- let status = getRateLimitStatus();
72
- expect(status.api.count).toBe(2);
73
- expect(status.commands.count).toBe(3);
74
- // Reset
75
- resetRateLimits();
76
- status = getRateLimitStatus();
77
- expect(status.api.count).toBe(0);
78
- expect(status.commands.count).toBe(0);
79
- });
80
- it('should allow requests after reset', () => {
81
- // Fill up the limit
82
- for (let i = 0; i < 30; i++) {
83
- checkApiRateLimit();
84
- }
85
- // Should be blocked
86
- expect(checkApiRateLimit().allowed).toBe(false);
87
- // Reset
88
- resetRateLimits();
89
- // Should be allowed again
90
- expect(checkApiRateLimit().allowed).toBe(true);
91
- });
92
- });
93
- describe('getRateLimitStatus', () => {
94
- it('should return current counts and limits', () => {
95
- const status = getRateLimitStatus();
96
- expect(status.api).toHaveProperty('count');
97
- expect(status.api).toHaveProperty('limit');
98
- expect(status.commands).toHaveProperty('count');
99
- expect(status.commands).toHaveProperty('limit');
100
- });
101
- it('should return correct limits', () => {
102
- const status = getRateLimitStatus();
103
- expect(status.api.limit).toBe(30);
104
- expect(status.commands.limit).toBe(100);
105
- });
106
- it('should update count after requests', () => {
107
- expect(getRateLimitStatus().api.count).toBe(0);
108
- checkApiRateLimit();
109
- expect(getRateLimitStatus().api.count).toBe(1);
110
- checkApiRateLimit();
111
- expect(getRateLimitStatus().api.count).toBe(2);
112
- });
113
- });
114
- describe('sliding window behavior', () => {
115
- it('should expire old requests after window', async () => {
116
- // This test uses fake timers to simulate time passing
117
- vi.useFakeTimers();
118
- // Make some requests
119
- for (let i = 0; i < 30; i++) {
120
- checkApiRateLimit();
121
- }
122
- // Should be blocked
123
- expect(checkApiRateLimit().allowed).toBe(false);
124
- // Advance time past the window (60 seconds)
125
- vi.advanceTimersByTime(61000);
126
- // Should be allowed again (old requests expired)
127
- expect(checkApiRateLimit().allowed).toBe(true);
128
- vi.useRealTimers();
129
- });
130
- });
131
- });
@@ -1 +0,0 @@
1
- export {};