@codeguide/core 0.0.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.
Files changed (79) hide show
  1. package/__tests__/authentication.test.ts +259 -0
  2. package/__tests__/codeguide.test.ts +293 -0
  3. package/__tests__/services/base/base-service.test.ts +334 -0
  4. package/__tests__/services/generation/generation-service.test.ts +294 -0
  5. package/__tests__/services/usage/usage-service.test.ts +385 -0
  6. package/__tests__/simple.test.ts +10 -0
  7. package/__tests__/test-service.ts +37 -0
  8. package/api-service.ts +67 -0
  9. package/codeguide.ts +70 -0
  10. package/dist/__tests__/test-service.d.ts +12 -0
  11. package/dist/__tests__/test-service.js +32 -0
  12. package/dist/api-service.d.ts +8 -0
  13. package/dist/api-service.js +64 -0
  14. package/dist/codeguide.d.ts +14 -0
  15. package/dist/codeguide.js +53 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.js +31 -0
  18. package/dist/services/base/base-service.d.ts +30 -0
  19. package/dist/services/base/base-service.js +187 -0
  20. package/dist/services/base/index.d.ts +1 -0
  21. package/dist/services/base/index.js +5 -0
  22. package/dist/services/generation/generation-service.d.ts +15 -0
  23. package/dist/services/generation/generation-service.js +40 -0
  24. package/dist/services/generation/generation-types.d.ts +111 -0
  25. package/dist/services/generation/generation-types.js +2 -0
  26. package/dist/services/generation/index.d.ts +2 -0
  27. package/dist/services/generation/index.js +20 -0
  28. package/dist/services/index.d.ts +11 -0
  29. package/dist/services/index.js +45 -0
  30. package/dist/services/projects/index.d.ts +2 -0
  31. package/dist/services/projects/index.js +20 -0
  32. package/dist/services/projects/project-service.d.ts +14 -0
  33. package/dist/services/projects/project-service.js +53 -0
  34. package/dist/services/projects/project-types.d.ts +97 -0
  35. package/dist/services/projects/project-types.js +2 -0
  36. package/dist/services/repository-analysis/index.d.ts +2 -0
  37. package/dist/services/repository-analysis/index.js +20 -0
  38. package/dist/services/repository-analysis/repository-service.d.ts +10 -0
  39. package/dist/services/repository-analysis/repository-service.js +31 -0
  40. package/dist/services/repository-analysis/repository-types.d.ts +90 -0
  41. package/dist/services/repository-analysis/repository-types.js +2 -0
  42. package/dist/services/tasks/index.d.ts +2 -0
  43. package/dist/services/tasks/index.js +20 -0
  44. package/dist/services/tasks/task-service.d.ts +30 -0
  45. package/dist/services/tasks/task-service.js +105 -0
  46. package/dist/services/tasks/task-types.d.ts +144 -0
  47. package/dist/services/tasks/task-types.js +2 -0
  48. package/dist/services/usage/index.d.ts +2 -0
  49. package/dist/services/usage/index.js +20 -0
  50. package/dist/services/usage/usage-service.d.ts +14 -0
  51. package/dist/services/usage/usage-service.js +68 -0
  52. package/dist/services/usage/usage-types.d.ts +133 -0
  53. package/dist/services/usage/usage-types.js +2 -0
  54. package/dist/types.d.ts +42 -0
  55. package/dist/types.js +2 -0
  56. package/index.ts +12 -0
  57. package/jest.config.json +19 -0
  58. package/package.json +45 -0
  59. package/services/README.md +113 -0
  60. package/services/base/base-service.ts +230 -0
  61. package/services/base/index.ts +1 -0
  62. package/services/generation/generation-service.ts +81 -0
  63. package/services/generation/generation-types.ts +131 -0
  64. package/services/generation/index.ts +2 -0
  65. package/services/index.ts +22 -0
  66. package/services/projects/index.ts +2 -0
  67. package/services/projects/project-service.ts +67 -0
  68. package/services/projects/project-types.ts +108 -0
  69. package/services/repository-analysis/index.ts +2 -0
  70. package/services/repository-analysis/repository-service.ts +42 -0
  71. package/services/repository-analysis/repository-types.ts +99 -0
  72. package/services/tasks/index.ts +2 -0
  73. package/services/tasks/task-service.ts +143 -0
  74. package/services/tasks/task-types.ts +165 -0
  75. package/services/usage/index.ts +2 -0
  76. package/services/usage/usage-service.ts +96 -0
  77. package/services/usage/usage-types.ts +147 -0
  78. package/tsconfig.json +10 -0
  79. package/types.ts +51 -0
@@ -0,0 +1,334 @@
1
+ import { TestService } from '../../test-service'
2
+ import { APIServiceConfig, AuthenticationMethod } from '../../types'
3
+ import axios from 'axios'
4
+ import MockAdapter from 'axios-mock-adapter'
5
+
6
+ describe('BaseService', () => {
7
+ let mockAxios: MockAdapter
8
+ let testService: TestService
9
+
10
+ beforeEach(() => {
11
+ mockAxios = new MockAdapter(axios)
12
+ })
13
+
14
+ afterEach(() => {
15
+ mockAxios.restore()
16
+ })
17
+
18
+ describe('Authentication Priority', () => {
19
+ it('should prioritize database API key when available', () => {
20
+ const config: APIServiceConfig = {
21
+ baseUrl: 'https://api.codeguide.app',
22
+ databaseApiKey: 'sk_test123',
23
+ apiKey: 'legacy_key',
24
+ userId: 'user123',
25
+ jwtToken: 'jwt_token',
26
+ }
27
+
28
+ testService = new TestService(config)
29
+ const authMethod = testService.testGetAuthenticationMethod()
30
+
31
+ expect(authMethod).toEqual({
32
+ type: 'database-api-key',
33
+ priority: 1,
34
+ headers: {
35
+ Authorization: 'Bearer sk_test123',
36
+ 'Content-Type': 'application/json',
37
+ },
38
+ })
39
+ })
40
+
41
+ it('should use legacy API key + user ID when no database API key', () => {
42
+ const config: APIServiceConfig = {
43
+ baseUrl: 'https://api.codeguide.app',
44
+ apiKey: 'legacy_key',
45
+ userId: 'user123',
46
+ jwtToken: 'jwt_token',
47
+ }
48
+
49
+ testService = new TestService(config)
50
+ const authMethod = testService.testGetAuthenticationMethod()
51
+
52
+ expect(authMethod).toEqual({
53
+ type: 'legacy-api-key',
54
+ priority: 2,
55
+ headers: {
56
+ 'X-API-Key': 'legacy_key',
57
+ 'X-User-ID': 'user123',
58
+ 'Content-Type': 'application/json',
59
+ },
60
+ })
61
+ })
62
+
63
+ it('should use JWT token when no other auth methods available', () => {
64
+ const config: APIServiceConfig = {
65
+ baseUrl: 'https://api.codeguide.app',
66
+ jwtToken: 'jwt_token',
67
+ }
68
+
69
+ testService = new TestService(config)
70
+ const authMethod = testService.testGetAuthenticationMethod()
71
+
72
+ expect(authMethod).toEqual({
73
+ type: 'clerk-jwt',
74
+ priority: 3,
75
+ headers: {
76
+ Authorization: 'Bearer jwt_token',
77
+ 'Content-Type': 'application/json',
78
+ },
79
+ })
80
+ })
81
+
82
+ it('should fallback to legacy API key without user ID', () => {
83
+ const config: APIServiceConfig = {
84
+ baseUrl: 'https://api.codeguide.app',
85
+ apiKey: 'legacy_key',
86
+ }
87
+
88
+ testService = new TestService(config)
89
+ const authMethod = testService.testGetAuthenticationMethod()
90
+
91
+ expect(authMethod).toEqual({
92
+ type: 'legacy-api-key',
93
+ priority: 2,
94
+ headers: {
95
+ 'X-API-Key': 'legacy_key',
96
+ 'Content-Type': 'application/json',
97
+ },
98
+ })
99
+ })
100
+
101
+ it('should return null when no authentication methods available', () => {
102
+ const config: APIServiceConfig = {
103
+ baseUrl: 'https://api.codeguide.app',
104
+ }
105
+
106
+ testService = new TestService(config)
107
+ const authMethod = testService.testGetAuthenticationMethod()
108
+
109
+ expect(authMethod).toBeNull()
110
+ })
111
+ })
112
+
113
+ describe('Authentication Validation', () => {
114
+ it('should validate database API key format', () => {
115
+ const config: APIServiceConfig = {
116
+ baseUrl: 'https://api.codeguide.app',
117
+ databaseApiKey: 'invalid_key', // Missing sk_ prefix
118
+ }
119
+
120
+ testService = new TestService(config)
121
+ const result = testService.testValidateAuthentication()
122
+
123
+ expect(result.success).toBe(false)
124
+ expect(result.error).toBe('Database API key must start with "sk_"')
125
+ })
126
+
127
+ it('should validate legacy API key requires user ID', () => {
128
+ const config: APIServiceConfig = {
129
+ baseUrl: 'https://api.codeguide.app',
130
+ apiKey: 'legacy_key',
131
+ // No userId
132
+ }
133
+
134
+ testService = new TestService(config)
135
+ const result = testService.testValidateAuthentication()
136
+
137
+ expect(result.success).toBe(true)
138
+ expect(result.method?.type).toBe('legacy-api-key')
139
+ })
140
+
141
+ it('should return error when no authentication configured', () => {
142
+ const config: APIServiceConfig = {
143
+ baseUrl: 'https://api.codeguide.app',
144
+ }
145
+
146
+ testService = new TestService(config)
147
+ const result = testService.testValidateAuthentication()
148
+
149
+ expect(result.success).toBe(false)
150
+ expect(result.error).toContain('No authentication method configured')
151
+ })
152
+
153
+ it('should validate successful database API key authentication', () => {
154
+ const config: APIServiceConfig = {
155
+ baseUrl: 'https://api.codeguide.app',
156
+ databaseApiKey: 'sk_test123',
157
+ }
158
+
159
+ testService = new TestService(config)
160
+ const result = testService.testValidateAuthentication()
161
+
162
+ expect(result.success).toBe(true)
163
+ expect(result.method?.type).toBe('database-api-key')
164
+ })
165
+ })
166
+
167
+ describe('HTTP Methods', () => {
168
+ const config: APIServiceConfig = {
169
+ baseUrl: 'https://api.codeguide.app',
170
+ databaseApiKey: 'sk_test123',
171
+ }
172
+
173
+ beforeEach(() => {
174
+ testService = new TestService(config)
175
+ })
176
+
177
+ it('should make GET requests with authentication headers', async () => {
178
+ const mockResponse = { data: 'test' }
179
+ mockAxios.onGet('/v1/test').reply(200, mockResponse)
180
+
181
+ const result = await testService.testGet('/test')
182
+
183
+ expect(result).toEqual(mockResponse)
184
+ const request = mockAxios.history.get[0]
185
+ expect(request.headers?.Authorization).toBe('Bearer sk_test123')
186
+ expect(request.headers?.['Content-Type']).toBe('application/json')
187
+ })
188
+
189
+ it('should make POST requests with authentication headers', async () => {
190
+ const mockData = { name: 'test' }
191
+ const mockResponse = { id: 1, ...mockData }
192
+ mockAxios.onPost('/v1/test').reply(200, mockResponse)
193
+
194
+ const result = await testService.testPost('/test', mockData)
195
+
196
+ expect(result).toEqual(mockResponse)
197
+ const request = mockAxios.history.post[0]
198
+ expect(request.headers?.Authorization).toBe('Bearer sk_test123')
199
+ expect(request.headers?.['Content-Type']).toBe('application/json')
200
+ expect(JSON.parse(request.data)).toEqual(mockData)
201
+ })
202
+
203
+ it('should make PUT requests with authentication headers', async () => {
204
+ const mockData = { name: 'updated' }
205
+ const mockResponse = { id: 1, ...mockData }
206
+ mockAxios.onPut('/v1/test/1').reply(200, mockResponse)
207
+
208
+ const result = await testService.testPut('/test/1', mockData)
209
+
210
+ expect(result).toEqual(mockResponse)
211
+ const request = mockAxios.history.put[0]
212
+ expect(request.headers?.Authorization).toBe('Bearer sk_test123')
213
+ })
214
+
215
+ it('should make DELETE requests with authentication headers', async () => {
216
+ mockAxios.onDelete('/v1/test/1').reply(204)
217
+
218
+ const result = await testService.testDelete('/test/1')
219
+
220
+ expect(result).toBeUndefined()
221
+ const request = mockAxios.history.delete[0]
222
+ expect(request.headers?.Authorization).toBe('Bearer sk_test123')
223
+ })
224
+ })
225
+
226
+ describe('Error Handling', () => {
227
+ const config: APIServiceConfig = {
228
+ baseUrl: 'https://api.codeguide.app',
229
+ databaseApiKey: 'sk_test123',
230
+ }
231
+
232
+ beforeEach(() => {
233
+ testService = new TestService(config)
234
+ })
235
+
236
+ it('should handle 401 authentication errors', async () => {
237
+ mockAxios.onGet('/v1/test').reply(401, { detail: 'Invalid API key' })
238
+
239
+ await expect(testService.testGet('/test')).rejects.toThrow(
240
+ 'Database API key authentication failed: Invalid API key'
241
+ )
242
+ })
243
+
244
+ it('should handle 403 permission errors', async () => {
245
+ mockAxios.onGet('/v1/test').reply(403, { detail: 'Insufficient permissions' })
246
+
247
+ await expect(testService.testGet('/test')).rejects.toThrow(
248
+ 'Access denied: Insufficient permissions'
249
+ )
250
+ })
251
+
252
+ it('should handle 429 rate limiting errors', async () => {
253
+ mockAxios.onGet('/v1/test').reply(429, { detail: 'Too many requests' })
254
+
255
+ await expect(testService.testGet('/test')).rejects.toThrow(
256
+ 'Rate limit exceeded: Too many requests'
257
+ )
258
+ })
259
+
260
+ it('should handle usage limit errors', async () => {
261
+ mockAxios.onGet('/v1/test').reply(402, { detail: 'Insufficient credits' })
262
+
263
+ await expect(testService.testGet('/test')).rejects.toThrow(
264
+ 'Usage limit exceeded: Insufficient credits'
265
+ )
266
+ })
267
+
268
+ it('should handle generic API errors', async () => {
269
+ mockAxios.onGet('/v1/test').reply(500, { message: 'Internal server error' })
270
+
271
+ await expect(testService.testGet('/test')).rejects.toThrow('API Error: Internal server error')
272
+ })
273
+ })
274
+
275
+ describe('URL Building', () => {
276
+ const config: APIServiceConfig = {
277
+ baseUrl: 'https://api.codeguide.app',
278
+ databaseApiKey: 'sk_test123',
279
+ }
280
+
281
+ beforeEach(() => {
282
+ testService = new TestService(config)
283
+ })
284
+
285
+ it('should build URL with leading slash', () => {
286
+ const url = testService.testBuildUrl('/test')
287
+ expect(url).toBe('/test')
288
+ })
289
+
290
+ it('should build URL without leading slash', () => {
291
+ const url = (baseService as any).buildUrl('test')
292
+ expect(url).toBe('/test')
293
+ })
294
+ })
295
+
296
+ describe('Base URL Configuration', () => {
297
+ it('should add /v1 suffix to baseUrl if not present', () => {
298
+ const config: APIServiceConfig = {
299
+ baseUrl: 'https://api.codeguide.app',
300
+ databaseApiKey: 'sk_test123',
301
+ }
302
+
303
+ testService = new TestService(config)
304
+
305
+ // Access the internal client to check baseURL
306
+ const client = (testService as any).client
307
+ expect(client.defaults.baseURL).toBe('https://api.codeguide.app/v1')
308
+ })
309
+
310
+ it('should not add /v1 suffix if already present', () => {
311
+ const config: APIServiceConfig = {
312
+ baseUrl: 'https://api.codeguide.app/v1',
313
+ databaseApiKey: 'sk_test123',
314
+ }
315
+
316
+ testService = new TestService(config)
317
+
318
+ const client = (testService as any).client
319
+ expect(client.defaults.baseURL).toBe('https://api.codeguide.app/v1')
320
+ })
321
+
322
+ it('should handle baseUrl with trailing slash', () => {
323
+ const config: APIServiceConfig = {
324
+ baseUrl: 'https://api.codeguide.app/',
325
+ databaseApiKey: 'sk_test123',
326
+ }
327
+
328
+ testService = new TestService(config)
329
+
330
+ const client = (testService as any).client
331
+ expect(client.defaults.baseURL).toBe('https://api.codeguide.app/v1')
332
+ })
333
+ })
334
+ })
@@ -0,0 +1,294 @@
1
+ import { GenerationService } from '../../../services/generation/generation-service'
2
+ import { APIServiceConfig } from '../../../types'
3
+ import axios from 'axios'
4
+ import MockAdapter from 'axios-mock-adapter'
5
+
6
+ describe('GenerationService', () => {
7
+ let mockAxios: MockAdapter
8
+ let generationService: GenerationService
9
+ let config: APIServiceConfig
10
+
11
+ beforeEach(() => {
12
+ mockAxios = new MockAdapter(axios)
13
+ config = {
14
+ baseUrl: 'https://api.codeguide.app',
15
+ databaseApiKey: 'sk_test123',
16
+ }
17
+ generationService = new GenerationService(config)
18
+ })
19
+
20
+ afterEach(() => {
21
+ mockAxios.restore()
22
+ })
23
+
24
+ describe('refinePrompt', () => {
25
+ it('should refine prompt successfully', async () => {
26
+ const request = {
27
+ user_prompt: 'How to create a React component?',
28
+ language: 'typescript',
29
+ context: 'CLI testing',
30
+ }
31
+
32
+ const response = {
33
+ refined_prompt:
34
+ 'To create a React component in TypeScript, you can define a functional component with proper typing...',
35
+ original_prompt: 'How to create a React component?',
36
+ }
37
+
38
+ mockAxios.onPost('/v1/generation/refine-prompt', request).reply(200, response)
39
+
40
+ const result = await generationService.refinePrompt(request)
41
+
42
+ expect(result).toEqual(response)
43
+ })
44
+
45
+ it('should handle refine prompt errors', async () => {
46
+ const request = {
47
+ user_prompt: 'Invalid prompt',
48
+ }
49
+
50
+ mockAxios.onPost('/v1/generation/refine-prompt', request).reply(400, {
51
+ detail: 'Prompt cannot be empty',
52
+ })
53
+
54
+ await expect(generationService.refinePrompt(request)).rejects.toThrow(
55
+ 'API Error: Prompt cannot be empty'
56
+ )
57
+ })
58
+
59
+ it('should work with minimal request', async () => {
60
+ const request = {
61
+ user_prompt: 'Simple prompt',
62
+ }
63
+
64
+ const response = {
65
+ refined_prompt: 'Refined response',
66
+ original_prompt: 'Simple prompt',
67
+ }
68
+
69
+ mockAxios.onPost('/v1/generation/refine-prompt', request).reply(200, response)
70
+
71
+ const result = await generationService.refinePrompt(request)
72
+
73
+ expect(result).toEqual(response)
74
+ })
75
+ })
76
+
77
+ describe('generateCode', () => {
78
+ it('should generate code successfully', async () => {
79
+ const request = {
80
+ prompt: 'Create a function to calculate factorial',
81
+ language: 'javascript',
82
+ context: 'Math utility function',
83
+ }
84
+
85
+ const response = {
86
+ generated_code:
87
+ 'function factorial(n) {\n if (n <= 1) return 1;\n return n * factorial(n - 1);\n}',
88
+ explanation: 'This function calculates the factorial of a number using recursion.',
89
+ language: 'javascript',
90
+ }
91
+
92
+ mockAxios.onPost('/v1/generation/generate-code', request).reply(200, response)
93
+
94
+ const result = await generationService.generateCode(request)
95
+
96
+ expect(result).toEqual(response)
97
+ })
98
+
99
+ it('should handle code generation errors', async () => {
100
+ const request = {
101
+ prompt: 'Create malicious code',
102
+ language: 'javascript',
103
+ }
104
+
105
+ mockAxios.onPost('/v1/generation/generate-code', request).reply(400, {
106
+ detail: 'Request cannot be fulfilled due to safety constraints',
107
+ })
108
+
109
+ await expect(generationService.generateCode(request)).rejects.toThrow(
110
+ 'API Error: Request cannot be fulfilled due to safety constraints'
111
+ )
112
+ })
113
+ })
114
+
115
+ describe('refineCode', () => {
116
+ it('should refine code successfully', async () => {
117
+ const request = {
118
+ code: 'function add(a, b) { return a + b; }',
119
+ language: 'javascript',
120
+ refinement_prompt: 'Add input validation',
121
+ context: 'Utility function',
122
+ }
123
+
124
+ const response = {
125
+ refined_code:
126
+ 'function add(a, b) {\n if (typeof a !== "number" || typeof b !== "number") {\n throw new Error("Both arguments must be numbers");\n }\n return a + b;\n}',
127
+ explanation: 'Added type validation to ensure both arguments are numbers.',
128
+ original_code: 'function add(a, b) { return a + b; }',
129
+ language: 'javascript',
130
+ }
131
+
132
+ mockAxios.onPost('/v1/generation/refine-code', request).reply(200, response)
133
+
134
+ const result = await generationService.refineCode(request)
135
+
136
+ expect(result).toEqual(response)
137
+ })
138
+
139
+ it('should handle code refinement errors', async () => {
140
+ const request = {
141
+ code: 'invalid code syntax',
142
+ language: 'javascript',
143
+ }
144
+
145
+ mockAxios.onPost('/v1/generation/refine-code', request).reply(400, {
146
+ detail: 'Invalid code syntax',
147
+ })
148
+
149
+ await expect(generationService.refineCode(request)).rejects.toThrow(
150
+ 'API Error: Invalid code syntax'
151
+ )
152
+ })
153
+ })
154
+
155
+ describe('explainCode', () => {
156
+ it('should explain code successfully', async () => {
157
+ const request = {
158
+ code: 'const factorial = n => n <= 1 ? 1 : n * factorial(n - 1);',
159
+ language: 'javascript',
160
+ }
161
+
162
+ const response = {
163
+ explanation:
164
+ 'This is a recursive arrow function that calculates the factorial of a number.',
165
+ breakdown: [
166
+ {
167
+ line: 1,
168
+ explanation: 'Defines a constant factorial using arrow function syntax',
169
+ },
170
+ {
171
+ line: 1,
172
+ explanation: 'Uses ternary operator for base case (n <= 1) and recursive case',
173
+ },
174
+ ],
175
+ language: 'javascript',
176
+ }
177
+
178
+ mockAxios.onPost('/v1/generation/explain-code', request).reply(200, response)
179
+
180
+ const result = await generationService.explainCode(request)
181
+
182
+ expect(result).toEqual(response)
183
+ })
184
+
185
+ it('should handle code explanation errors', async () => {
186
+ const request = {
187
+ code: 'incomplete code',
188
+ language: 'python',
189
+ }
190
+
191
+ mockAxios.onPost('/v1/generation/explain-code', request).reply(400, {
192
+ detail: 'Cannot explain incomplete code',
193
+ })
194
+
195
+ await expect(generationService.explainCode(request)).rejects.toThrow(
196
+ 'API Error: Cannot explain incomplete code'
197
+ )
198
+ })
199
+ })
200
+
201
+ describe('getSuggestions', () => {
202
+ it('should get code suggestions successfully', async () => {
203
+ const request = {
204
+ code: 'function calculateSum(arr) {\n // TODO: Implement sum calculation\n}',
205
+ language: 'javascript',
206
+ cursor_position: 50,
207
+ context: 'Array processing function',
208
+ }
209
+
210
+ const response = {
211
+ suggestions: [
212
+ {
213
+ suggestion: 'return arr.reduce((sum, num) => sum + num, 0);',
214
+ explanation: 'Use reduce method to calculate sum of array elements',
215
+ confidence: 0.95,
216
+ },
217
+ {
218
+ suggestion: 'let sum = 0;\nfor (const num of arr) {\n sum += num;\n}\nreturn sum;',
219
+ explanation: 'Use traditional for loop for better performance with large arrays',
220
+ confidence: 0.85,
221
+ },
222
+ ],
223
+ language: 'javascript',
224
+ }
225
+
226
+ mockAxios.onPost('/v1/generation/suggestions', request).reply(200, response)
227
+
228
+ const result = await generationService.getSuggestions(request)
229
+
230
+ expect(result).toEqual(response)
231
+ })
232
+
233
+ it('should handle suggestion request errors', async () => {
234
+ const request = {
235
+ code: 'invalid code',
236
+ language: 'javascript',
237
+ }
238
+
239
+ mockAxios.onPost('/v1/generation/suggestions', request).reply(400, {
240
+ detail: 'Cannot generate suggestions for invalid code',
241
+ })
242
+
243
+ await expect(generationService.getSuggestions(request)).rejects.toThrow(
244
+ 'API Error: Cannot generate suggestions for invalid code'
245
+ )
246
+ })
247
+ })
248
+
249
+ describe('Authentication Integration', () => {
250
+ it('should use correct authentication headers for all requests', async () => {
251
+ const request = {
252
+ user_prompt: 'Test prompt',
253
+ }
254
+
255
+ const response = {
256
+ refined_prompt: 'Test response',
257
+ }
258
+
259
+ mockAxios.onPost('/v1/generation/refine-prompt', request).reply(200, response)
260
+
261
+ await generationService.refinePrompt(request)
262
+
263
+ const mockRequest = mockAxios.history.post[0]
264
+ expect(mockRequest.headers?.Authorization).toBe('Bearer sk_test123')
265
+ expect(mockRequest.headers?.['Content-Type']).toBe('application/json')
266
+ })
267
+
268
+ it('should work with different authentication methods', async () => {
269
+ const legacyConfig: APIServiceConfig = {
270
+ baseUrl: 'https://api.codeguide.app',
271
+ apiKey: 'legacy_key',
272
+ userId: 'user123',
273
+ }
274
+
275
+ const legacyService = new GenerationService(legacyConfig)
276
+
277
+ const request = {
278
+ user_prompt: 'Test prompt',
279
+ }
280
+
281
+ const response = {
282
+ refined_prompt: 'Test response',
283
+ }
284
+
285
+ mockAxios.onPost('/v1/generation/refine-prompt', request).reply(200, response)
286
+
287
+ await legacyService.refinePrompt(request)
288
+
289
+ const mockRequest = mockAxios.history.post[0]
290
+ expect(mockRequest.headers?.['X-API-Key']).toBe('legacy_key')
291
+ expect(mockRequest.headers?.['X-User-ID']).toBe('user123')
292
+ })
293
+ })
294
+ })