promptfoo 0.103.13 → 0.103.14

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 (84) hide show
  1. package/dist/package.json +13 -13
  2. package/dist/src/app/assets/{index-CNZfEf8i.js → index-gCO1so_L.js} +233 -232
  3. package/dist/src/app/assets/{index.es-vrsSPryr.js → index.es-BebRurvf.js} +1 -1
  4. package/dist/src/app/assets/{sync-B_Y0n4tJ.js → sync-cefC8sLm.js} +1 -1
  5. package/dist/src/app/index.html +1 -1
  6. package/dist/src/fetch.d.ts +2 -0
  7. package/dist/src/fetch.d.ts.map +1 -1
  8. package/dist/src/fetch.js +2 -1
  9. package/dist/src/fetch.js.map +1 -1
  10. package/dist/src/providers/azure.d.ts.map +1 -1
  11. package/dist/src/providers/azure.js +10 -0
  12. package/dist/src/providers/azure.js.map +1 -1
  13. package/dist/src/providers/http.d.ts +5 -3
  14. package/dist/src/providers/http.d.ts.map +1 -1
  15. package/dist/src/providers/http.js +7 -6
  16. package/dist/src/providers/http.js.map +1 -1
  17. package/dist/src/redteam/commands/generate.d.ts.map +1 -1
  18. package/dist/src/redteam/commands/generate.js +30 -9
  19. package/dist/src/redteam/commands/generate.js.map +1 -1
  20. package/dist/src/redteam/graders.d.ts.map +1 -1
  21. package/dist/src/redteam/graders.js +13 -13
  22. package/dist/src/redteam/graders.js.map +1 -1
  23. package/dist/src/redteam/index.d.ts +1 -0
  24. package/dist/src/redteam/index.d.ts.map +1 -1
  25. package/dist/src/redteam/index.js +1 -1
  26. package/dist/src/redteam/index.js.map +1 -1
  27. package/dist/src/redteam/plugins/harmful/graders.d.ts +52 -0
  28. package/dist/src/redteam/plugins/harmful/graders.d.ts.map +1 -1
  29. package/dist/src/redteam/plugins/harmful/graders.js +577 -22
  30. package/dist/src/redteam/plugins/harmful/graders.js.map +1 -1
  31. package/dist/src/redteam/types.d.ts +1 -0
  32. package/dist/src/redteam/types.d.ts.map +1 -1
  33. package/dist/src/redteam/util.d.ts.map +1 -1
  34. package/dist/src/redteam/util.js +2 -0
  35. package/dist/src/redteam/util.js.map +1 -1
  36. package/dist/src/server/server.d.ts.map +1 -1
  37. package/dist/src/server/server.js +1 -7
  38. package/dist/src/server/server.js.map +1 -1
  39. package/dist/src/validators/redteam.d.ts +3 -0
  40. package/dist/src/validators/redteam.d.ts.map +1 -1
  41. package/dist/src/validators/redteam.js +2 -0
  42. package/dist/src/validators/redteam.js.map +1 -1
  43. package/dist/test/assertions/answerRelevance.test.d.ts +2 -0
  44. package/dist/test/assertions/answerRelevance.test.d.ts.map +1 -0
  45. package/dist/test/assertions/answerRelevance.test.js +177 -0
  46. package/dist/test/assertions/answerRelevance.test.js.map +1 -0
  47. package/dist/test/assertions/contextFaithfulness.test.d.ts +2 -0
  48. package/dist/test/assertions/contextFaithfulness.test.d.ts.map +1 -0
  49. package/dist/test/assertions/contextFaithfulness.test.js +226 -0
  50. package/dist/test/assertions/contextFaithfulness.test.js.map +1 -0
  51. package/dist/test/assertions/contextRecall.test.d.ts +2 -0
  52. package/dist/test/assertions/contextRecall.test.d.ts.map +1 -0
  53. package/dist/test/assertions/contextRecall.test.js +243 -0
  54. package/dist/test/assertions/contextRecall.test.js.map +1 -0
  55. package/dist/test/assertions/contextRelevance.test.d.ts +2 -0
  56. package/dist/test/assertions/contextRelevance.test.d.ts.map +1 -0
  57. package/dist/test/assertions/contextRelevance.test.js +238 -0
  58. package/dist/test/assertions/contextRelevance.test.js.map +1 -0
  59. package/dist/test/assertions/geval.test.d.ts +2 -0
  60. package/dist/test/assertions/geval.test.d.ts.map +1 -0
  61. package/dist/test/assertions/geval.test.js +222 -0
  62. package/dist/test/assertions/geval.test.js.map +1 -0
  63. package/dist/test/assertions/modelGradedClosedQa.test.d.ts +2 -0
  64. package/dist/test/assertions/modelGradedClosedQa.test.d.ts.map +1 -0
  65. package/dist/test/assertions/modelGradedClosedQa.test.js +200 -0
  66. package/dist/test/assertions/modelGradedClosedQa.test.js.map +1 -0
  67. package/dist/test/fetch.test.js +66 -18
  68. package/dist/test/fetch.test.js.map +1 -1
  69. package/dist/test/providers/azure.test.js +41 -11
  70. package/dist/test/providers/azure.test.js.map +1 -1
  71. package/dist/test/providers/http.test.js +70 -2
  72. package/dist/test/providers/http.test.js.map +1 -1
  73. package/dist/test/providers/index.test.js +0 -454
  74. package/dist/test/providers/index.test.js.map +1 -1
  75. package/dist/test/providers/openai.test.js +509 -0
  76. package/dist/test/providers/openai.test.js.map +1 -1
  77. package/dist/test/redteam/commands/generate.test.js +7 -0
  78. package/dist/test/redteam/commands/generate.test.js.map +1 -1
  79. package/dist/test/redteam/extraction/purpose.test.js +1 -0
  80. package/dist/test/redteam/extraction/purpose.test.js.map +1 -1
  81. package/dist/test/redteam/strategies/index.test.js +1 -0
  82. package/dist/test/redteam/strategies/index.test.js.map +1 -1
  83. package/dist/tsconfig.tsbuildinfo +1 -1
  84. package/package.json +13 -13
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const geval_1 = require("../../src/assertions/geval");
4
+ const matchers_1 = require("../../src/matchers");
5
+ jest.mock('../../src/matchers');
6
+ describe('handleGEval', () => {
7
+ beforeEach(() => {
8
+ jest.resetAllMocks();
9
+ });
10
+ it('should handle string renderedValue', async () => {
11
+ const mockMatchesGEval = jest.mocked(matchers_1.matchesGEval);
12
+ mockMatchesGEval.mockResolvedValue({
13
+ pass: true,
14
+ score: 0.8,
15
+ reason: 'test reason',
16
+ });
17
+ const result = await (0, geval_1.handleGEval)({
18
+ assertion: {
19
+ type: 'g-eval',
20
+ value: 'test criteria',
21
+ threshold: 0.7,
22
+ },
23
+ renderedValue: 'test criteria',
24
+ prompt: 'test prompt',
25
+ outputString: 'test output',
26
+ test: {
27
+ vars: {},
28
+ assert: [],
29
+ options: {},
30
+ },
31
+ baseType: 'g-eval',
32
+ context: {
33
+ prompt: 'test prompt',
34
+ vars: {},
35
+ test: {
36
+ vars: {},
37
+ assert: [],
38
+ options: {},
39
+ },
40
+ logProbs: undefined,
41
+ provider: {
42
+ id: () => 'test-provider',
43
+ callApi: async () => ({ output: 'test' }),
44
+ },
45
+ providerResponse: {
46
+ output: 'test output',
47
+ error: undefined,
48
+ },
49
+ },
50
+ inverse: false,
51
+ output: 'test output',
52
+ providerResponse: {
53
+ output: 'test output',
54
+ error: undefined,
55
+ },
56
+ });
57
+ expect(result).toEqual({
58
+ assertion: {
59
+ type: 'g-eval',
60
+ value: 'test criteria',
61
+ threshold: 0.7,
62
+ },
63
+ pass: true,
64
+ score: 0.8,
65
+ reason: 'test reason',
66
+ });
67
+ expect(mockMatchesGEval).toHaveBeenCalledWith('test criteria', 'test prompt', 'test output', 0.7, {});
68
+ });
69
+ it('should handle array renderedValue', async () => {
70
+ const mockMatchesGEval = jest.mocked(matchers_1.matchesGEval);
71
+ mockMatchesGEval.mockResolvedValueOnce({
72
+ pass: true,
73
+ score: 0.8,
74
+ reason: 'test reason 1',
75
+ });
76
+ mockMatchesGEval.mockResolvedValueOnce({
77
+ pass: false,
78
+ score: 0.6,
79
+ reason: 'test reason 2',
80
+ });
81
+ const result = await (0, geval_1.handleGEval)({
82
+ assertion: {
83
+ type: 'g-eval',
84
+ value: ['criteria1', 'criteria2'],
85
+ threshold: 0.7,
86
+ },
87
+ renderedValue: ['criteria1', 'criteria2'],
88
+ prompt: 'test prompt',
89
+ outputString: 'test output',
90
+ test: {
91
+ vars: {},
92
+ assert: [],
93
+ options: {},
94
+ },
95
+ baseType: 'g-eval',
96
+ context: {
97
+ prompt: 'test prompt',
98
+ vars: {},
99
+ test: {
100
+ vars: {},
101
+ assert: [],
102
+ options: {},
103
+ },
104
+ logProbs: undefined,
105
+ provider: {
106
+ id: () => 'test-provider',
107
+ callApi: async () => ({ output: 'test' }),
108
+ },
109
+ providerResponse: {
110
+ output: 'test output',
111
+ error: undefined,
112
+ },
113
+ },
114
+ inverse: false,
115
+ output: 'test output',
116
+ providerResponse: {
117
+ output: 'test output',
118
+ error: undefined,
119
+ },
120
+ });
121
+ expect(result).toEqual({
122
+ assertion: {
123
+ type: 'g-eval',
124
+ value: ['criteria1', 'criteria2'],
125
+ threshold: 0.7,
126
+ },
127
+ pass: true,
128
+ score: 0.7,
129
+ reason: 'test reason 2',
130
+ });
131
+ });
132
+ it('should use default threshold if not provided', async () => {
133
+ const mockMatchesGEval = jest.mocked(matchers_1.matchesGEval);
134
+ mockMatchesGEval.mockResolvedValue({
135
+ pass: true,
136
+ score: 0.8,
137
+ reason: 'test reason',
138
+ });
139
+ await (0, geval_1.handleGEval)({
140
+ assertion: {
141
+ type: 'g-eval',
142
+ value: 'test criteria',
143
+ },
144
+ renderedValue: 'test criteria',
145
+ prompt: 'test prompt',
146
+ outputString: 'test output',
147
+ test: {
148
+ vars: {},
149
+ assert: [],
150
+ options: {},
151
+ },
152
+ baseType: 'g-eval',
153
+ context: {
154
+ prompt: 'test prompt',
155
+ vars: {},
156
+ test: {
157
+ vars: {},
158
+ assert: [],
159
+ options: {},
160
+ },
161
+ logProbs: undefined,
162
+ provider: {
163
+ id: () => 'test-provider',
164
+ callApi: async () => ({ output: 'test' }),
165
+ },
166
+ providerResponse: {
167
+ output: 'test output',
168
+ error: undefined,
169
+ },
170
+ },
171
+ inverse: false,
172
+ output: 'test output',
173
+ providerResponse: {
174
+ output: 'test output',
175
+ error: undefined,
176
+ },
177
+ });
178
+ expect(mockMatchesGEval).toHaveBeenCalledWith('test criteria', 'test prompt', 'test output', 0.7, {});
179
+ });
180
+ it('should throw error for invalid renderedValue type', async () => {
181
+ await expect((0, geval_1.handleGEval)({
182
+ assertion: {
183
+ type: 'g-eval',
184
+ value: 'test',
185
+ },
186
+ renderedValue: undefined,
187
+ prompt: 'test',
188
+ outputString: 'test',
189
+ test: {
190
+ vars: {},
191
+ assert: [],
192
+ options: {},
193
+ },
194
+ baseType: 'g-eval',
195
+ context: {
196
+ prompt: 'test prompt',
197
+ vars: {},
198
+ test: {
199
+ vars: {},
200
+ assert: [],
201
+ options: {},
202
+ },
203
+ logProbs: undefined,
204
+ provider: {
205
+ id: () => 'test-provider',
206
+ callApi: async () => ({ output: 'test' }),
207
+ },
208
+ providerResponse: {
209
+ output: 'test output',
210
+ error: undefined,
211
+ },
212
+ },
213
+ inverse: false,
214
+ output: 'test',
215
+ providerResponse: {
216
+ output: 'test',
217
+ error: undefined,
218
+ },
219
+ })).rejects.toThrow('G-Eval assertion type must have a string or array of strings value');
220
+ });
221
+ });
222
+ //# sourceMappingURL=geval.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geval.test.js","sourceRoot":"","sources":["../../../test/assertions/geval.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=modelGradedClosedQa.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelGradedClosedQa.test.d.ts","sourceRoot":"","sources":["../../../test/assertions/modelGradedClosedQa.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const modelGradedClosedQa_1 = require("../../src/assertions/modelGradedClosedQa");
4
+ const matchers_1 = require("../../src/matchers");
5
+ const templates_1 = require("../../src/util/templates");
6
+ jest.mock('../../src/matchers');
7
+ jest.mock('../../src/util/templates');
8
+ describe('handleModelGradedClosedQa', () => {
9
+ beforeEach(() => {
10
+ jest.resetAllMocks();
11
+ const mockNunjucksEnv = {
12
+ options: { autoescape: true },
13
+ render: jest.fn(),
14
+ renderString: jest.fn().mockImplementation((str) => str),
15
+ addFilter: jest.fn(),
16
+ getFilter: jest.fn(),
17
+ hasExtension: jest.fn(),
18
+ addExtension: jest.fn(),
19
+ removeExtension: jest.fn(),
20
+ getExtension: jest.fn(),
21
+ addGlobal: jest.fn(),
22
+ getGlobal: jest.fn(),
23
+ getTemplate: jest.fn(),
24
+ express: jest.fn(),
25
+ on: jest.fn(),
26
+ };
27
+ jest.mocked(templates_1.getNunjucksEngine).mockReturnValue(mockNunjucksEnv);
28
+ jest.mocked(matchers_1.matchesClosedQa).mockResolvedValue({
29
+ pass: true,
30
+ score: 1,
31
+ reason: 'test reason',
32
+ });
33
+ });
34
+ it('should validate string value', async () => {
35
+ const params = {
36
+ assertion: { type: 'model-graded-closedqa' },
37
+ baseType: 'model-graded-closedqa',
38
+ context: {
39
+ prompt: 'test prompt',
40
+ vars: {},
41
+ test: { vars: {} },
42
+ logProbs: undefined,
43
+ provider: undefined,
44
+ providerResponse: undefined,
45
+ },
46
+ inverse: false,
47
+ output: 'test output',
48
+ outputString: 'test output',
49
+ prompt: 'test prompt',
50
+ providerResponse: {},
51
+ renderedValue: {},
52
+ test: {
53
+ options: {},
54
+ vars: {},
55
+ },
56
+ };
57
+ await expect((0, modelGradedClosedQa_1.handleModelGradedClosedQa)(params)).rejects.toThrow('model-graded-closedqa assertion type must have a string value');
58
+ });
59
+ it('should validate prompt exists', async () => {
60
+ const params = {
61
+ assertion: { type: 'model-graded-closedqa' },
62
+ baseType: 'model-graded-closedqa',
63
+ context: {
64
+ prompt: undefined,
65
+ vars: {},
66
+ test: { vars: {} },
67
+ logProbs: undefined,
68
+ provider: undefined,
69
+ providerResponse: undefined,
70
+ },
71
+ inverse: false,
72
+ output: 'test output',
73
+ outputString: 'test output',
74
+ prompt: undefined,
75
+ providerResponse: {},
76
+ renderedValue: 'test value',
77
+ test: {
78
+ options: {},
79
+ vars: {},
80
+ },
81
+ };
82
+ await expect((0, modelGradedClosedQa_1.handleModelGradedClosedQa)(params)).rejects.toThrow('model-graded-closedqa assertion type must have a prompt');
83
+ });
84
+ it('should validate rubricPrompt is string if provided', async () => {
85
+ const params = {
86
+ assertion: { type: 'model-graded-closedqa' },
87
+ baseType: 'model-graded-closedqa',
88
+ context: {
89
+ prompt: 'test prompt',
90
+ vars: {},
91
+ test: { vars: {} },
92
+ logProbs: undefined,
93
+ provider: undefined,
94
+ providerResponse: undefined,
95
+ },
96
+ inverse: false,
97
+ output: 'test output',
98
+ outputString: 'test output',
99
+ prompt: 'test prompt',
100
+ providerResponse: {},
101
+ renderedValue: 'test value',
102
+ test: {
103
+ options: {
104
+ rubricPrompt: {},
105
+ },
106
+ vars: {},
107
+ },
108
+ };
109
+ await expect((0, modelGradedClosedQa_1.handleModelGradedClosedQa)(params)).rejects.toThrow('rubricPrompt must be a string');
110
+ });
111
+ it('should process rubricPrompt with nunjucks if provided', async () => {
112
+ const mockRenderString = jest.fn().mockReturnValue('rendered rubric');
113
+ const mockNunjucksEnv = {
114
+ options: { autoescape: true },
115
+ render: jest.fn(),
116
+ renderString: mockRenderString,
117
+ addFilter: jest.fn(),
118
+ getFilter: jest.fn(),
119
+ hasExtension: jest.fn(),
120
+ addExtension: jest.fn(),
121
+ removeExtension: jest.fn(),
122
+ getExtension: jest.fn(),
123
+ addGlobal: jest.fn(),
124
+ getGlobal: jest.fn(),
125
+ getTemplate: jest.fn(),
126
+ express: jest.fn(),
127
+ on: jest.fn(),
128
+ };
129
+ jest.mocked(templates_1.getNunjucksEngine).mockReturnValue(mockNunjucksEnv);
130
+ const params = {
131
+ assertion: { type: 'model-graded-closedqa' },
132
+ baseType: 'model-graded-closedqa',
133
+ context: {
134
+ prompt: 'test prompt',
135
+ vars: { var: 'value' },
136
+ test: { vars: { var: 'value' } },
137
+ logProbs: undefined,
138
+ provider: undefined,
139
+ providerResponse: undefined,
140
+ },
141
+ inverse: false,
142
+ output: 'test output',
143
+ outputString: 'test output',
144
+ prompt: 'test prompt',
145
+ providerResponse: {},
146
+ renderedValue: 'test value',
147
+ test: {
148
+ options: {
149
+ rubricPrompt: 'test rubric {{ var }}',
150
+ },
151
+ vars: {
152
+ var: 'value',
153
+ },
154
+ },
155
+ };
156
+ await (0, modelGradedClosedQa_1.handleModelGradedClosedQa)(params);
157
+ expect(mockRenderString).toHaveBeenCalledWith('test rubric {{ var }}', { var: 'value' });
158
+ });
159
+ it('should call matchesClosedQa with correct parameters', async () => {
160
+ const params = {
161
+ assertion: { type: 'model-graded-closedqa' },
162
+ baseType: 'model-graded-closedqa',
163
+ context: {
164
+ prompt: 'test prompt',
165
+ vars: { var: 'value' },
166
+ test: { vars: { var: 'value' } },
167
+ logProbs: undefined,
168
+ provider: undefined,
169
+ providerResponse: undefined,
170
+ },
171
+ inverse: false,
172
+ output: 'test output',
173
+ outputString: 'test output',
174
+ prompt: 'test prompt',
175
+ providerResponse: {},
176
+ renderedValue: 'test value',
177
+ test: {
178
+ options: {
179
+ rubricPrompt: 'test rubric',
180
+ },
181
+ vars: {
182
+ var: 'value',
183
+ },
184
+ },
185
+ };
186
+ const result = await (0, modelGradedClosedQa_1.handleModelGradedClosedQa)(params);
187
+ expect(matchers_1.matchesClosedQa).toHaveBeenCalledWith('test prompt', 'test value', 'test output', {
188
+ rubricPrompt: 'test rubric',
189
+ }, {
190
+ var: 'value',
191
+ });
192
+ expect(result).toEqual({
193
+ assertion: { type: 'model-graded-closedqa' },
194
+ pass: true,
195
+ score: 1,
196
+ reason: 'test reason',
197
+ });
198
+ });
199
+ });
200
+ //# sourceMappingURL=modelGradedClosedQa.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modelGradedClosedQa.test.js","sourceRoot":"","sources":["../../../test/assertions/modelGradedClosedQa.test.ts"],"names":[],"mappings":"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"}
@@ -30,7 +30,7 @@ jest.mock('../src/logger');
30
30
  jest.mock('../src/envars', () => ({
31
31
  getEnvString: jest.fn().mockImplementation((key, defaultValue = '') => ''),
32
32
  getEnvBool: jest.fn().mockImplementation((key, defaultValue = false) => false),
33
- getEnvInt: jest.fn().mockReturnValue(100),
33
+ getEnvInt: jest.fn().mockImplementation((key, defaultValue = 0) => defaultValue),
34
34
  }));
35
35
  jest.mock('fs', () => ({
36
36
  readFileSync: jest.fn(),
@@ -266,7 +266,6 @@ describe('fetchWithProxy', () => {
266
266
  const mockCertContent = 'mock-cert-content';
267
267
  const mockProxyUrl = 'http://proxy.example.com';
268
268
  process.env.HTTPS_PROXY = mockProxyUrl;
269
- // Update basePath in cliState
270
269
  cliState_1.default.basePath = mockBasePath;
271
270
  jest.mocked(envars_1.getEnvString).mockImplementation((key, defaultValue = '') => {
272
271
  if (key === 'PROMPTFOO_CA_CERT_PATH') {
@@ -307,7 +306,6 @@ describe('fetchWithProxy', () => {
307
306
  },
308
307
  });
309
308
  expect(undici_1.setGlobalDispatcher).toHaveBeenCalledWith(expect.any(undici_1.ProxyAgent));
310
- // Reset basePath
311
309
  cliState_1.default.basePath = undefined;
312
310
  });
313
311
  it('should not create ProxyAgent when no proxy URL is found', async () => {
@@ -324,6 +322,10 @@ describe('fetchWithProxy', () => {
324
322
  };
325
323
  const allProxyVars = ['HTTPS_PROXY', 'https_proxy', 'HTTP_PROXY', 'http_proxy'];
326
324
  const testCases = [
325
+ {
326
+ env: { HTTPS_PROXY: mockProxyUrls.HTTPS_PROXY },
327
+ expected: { url: mockProxyUrls.HTTPS_PROXY },
328
+ },
327
329
  {
328
330
  env: { https_proxy: mockProxyUrls.https_proxy },
329
331
  expected: { url: mockProxyUrls.https_proxy },
@@ -342,10 +344,6 @@ describe('fetchWithProxy', () => {
342
344
  allProxyVars.forEach((key) => {
343
345
  delete process.env[key];
344
346
  });
345
- const existingProxyVars = allProxyVars.filter((key) => process.env[key]);
346
- if (existingProxyVars.length > 0) {
347
- console.warn('Found unexpected proxy variables:', existingProxyVars);
348
- }
349
347
  Object.entries(testCase.env).forEach(([key, value]) => {
350
348
  process.env[key] = value;
351
349
  });
@@ -361,12 +359,15 @@ describe('fetchWithProxy', () => {
361
359
  });
362
360
  expect(undici_1.setGlobalDispatcher).toHaveBeenCalledWith(expect.any(undici_1.ProxyAgent));
363
361
  const debugCalls = jest.mocked(logger_1.default.debug).mock.calls;
364
- expect(debugCalls).toEqual(expect.arrayContaining([
365
- [
366
- expect.stringMatching(new RegExp(`Found proxy configuration in .*https?_proxy.*: ${testCase.expected.url}/`, 'i')),
367
- ],
368
- [`Using proxy: ${testCase.expected.url}/`],
369
- ]));
362
+ const normalizedCalls = debugCalls.map((call) => call[0].replace(/\/$/, ''));
363
+ // On Windows, environment variables are case-insensitive
364
+ const isWindows = process.platform === 'win32';
365
+ const expectedEnvVar = Object.keys(testCase.env)[0];
366
+ const expectedUrl = testCase.expected.url;
367
+ expect(normalizedCalls).toEqual([
368
+ expect.stringMatching(new RegExp(`Found proxy configuration in ${isWindows ? '.*' : expectedEnvVar}: ${expectedUrl}`, isWindows ? 'i' : '')),
369
+ `Using proxy: ${expectedUrl}`,
370
+ ]);
370
371
  allProxyVars.forEach((key) => {
371
372
  delete process.env[key];
372
373
  });
@@ -508,14 +509,14 @@ describe('fetchWithRetries', () => {
508
509
  it('should make retries+1 total attempts', async () => {
509
510
  jest.mocked(global.fetch).mockRejectedValue(new Error('Network error'));
510
511
  await expect((0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 2)).rejects.toThrow('Request failed after 2 retries: Network error');
511
- expect(global.fetch).toHaveBeenCalledTimes(3); // Initial attempt + 2 retries
512
- expect(time_1.sleep).toHaveBeenCalledTimes(2); // Should sleep between attempts, but not after last attempt
512
+ expect(global.fetch).toHaveBeenCalledTimes(3);
513
+ expect(time_1.sleep).toHaveBeenCalledTimes(2);
513
514
  });
514
515
  it('should not sleep after the final attempt', async () => {
515
516
  jest.mocked(global.fetch).mockRejectedValue(new Error('Network error'));
516
517
  await expect((0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 1)).rejects.toThrow('Request failed after 1 retries: Network error');
517
- expect(global.fetch).toHaveBeenCalledTimes(2); // Initial attempt + 1 retry
518
- expect(time_1.sleep).toHaveBeenCalledTimes(1); // Should only sleep once between attempts
518
+ expect(global.fetch).toHaveBeenCalledTimes(2);
519
+ expect(time_1.sleep).toHaveBeenCalledTimes(1);
519
520
  });
520
521
  it('should handle 5XX errors when PROMPTFOO_RETRY_5XX is true', async () => {
521
522
  jest.mocked(envars_1.getEnvBool).mockImplementation((key) => {
@@ -560,8 +561,55 @@ describe('fetchWithRetries', () => {
560
561
  const mockFetch = jest.fn().mockRejectedValue(new Error('Network error'));
561
562
  global.fetch = mockFetch;
562
563
  await expect((0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 2)).rejects.toThrow('Request failed after 2 retries: Network error');
563
- expect(mockFetch).toHaveBeenCalledTimes(3); // Initial attempt + 2 retries
564
+ expect(mockFetch).toHaveBeenCalledTimes(3);
564
565
  expect(time_1.sleep).toHaveBeenCalledTimes(2);
565
566
  });
567
+ it('should handle detailed error information', async () => {
568
+ const error = new Error('Network error');
569
+ error.code = 'ECONNREFUSED';
570
+ error.cause = 'Connection refused';
571
+ jest.mocked(global.fetch).mockRejectedValue(error);
572
+ await expect((0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 1)).rejects.toThrow('Request failed after 1 retries: Network error');
573
+ expect(global.fetch).toHaveBeenCalledTimes(2);
574
+ expect(time_1.sleep).toHaveBeenCalledTimes(1);
575
+ });
576
+ it('should handle non-Error objects in rejection', async () => {
577
+ jest.mocked(global.fetch).mockRejectedValue('String error');
578
+ await expect((0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 1)).rejects.toThrow('Request failed after 1 retries: undefined');
579
+ expect(global.fetch).toHaveBeenCalledTimes(2);
580
+ expect(time_1.sleep).toHaveBeenCalledTimes(1);
581
+ });
582
+ it('should handle rate limits with OpenAI specific headers', async () => {
583
+ const rateLimitedResponse = (0, utils_1.createMockResponse)({
584
+ status: 429,
585
+ headers: new Headers({
586
+ 'x-ratelimit-reset-tokens': '5',
587
+ }),
588
+ });
589
+ const successResponse = (0, utils_1.createMockResponse)();
590
+ const mockFetch = jest
591
+ .fn()
592
+ .mockResolvedValueOnce(rateLimitedResponse)
593
+ .mockResolvedValueOnce(successResponse);
594
+ global.fetch = mockFetch;
595
+ await (0, fetch_1.fetchWithRetries)('https://example.com', {}, 1000, 2);
596
+ expect(mockFetch).toHaveBeenCalledTimes(2);
597
+ expect(logger_1.default.debug).toHaveBeenCalledWith(expect.stringContaining('Rate limited on URL'));
598
+ expect(time_1.sleep).toHaveBeenCalledTimes(1);
599
+ });
600
+ });
601
+ describe('sanitizeUrl', () => {
602
+ it('should mask credentials in URLs', () => {
603
+ const url = 'https://username:password@example.com/api';
604
+ expect((0, fetch_1.sanitizeUrl)(url)).toBe('https://***:***@example.com/api');
605
+ });
606
+ it('should handle URLs without credentials', () => {
607
+ const url = 'https://example.com/api';
608
+ expect((0, fetch_1.sanitizeUrl)(url)).toBe(url);
609
+ });
610
+ it('should return original string for invalid URLs', () => {
611
+ const invalidUrl = 'not-a-url';
612
+ expect((0, fetch_1.sanitizeUrl)(invalidUrl)).toBe(invalidUrl);
613
+ });
566
614
  });
567
615
  //# sourceMappingURL=fetch.test.js.map