Register
Login
Resources
Docs Blog Datasets Glossary Case Studies Tutorials & Webinars
Product
Data Engine LLMs Platform Enterprise
Pricing Explore
Connect to our Discord channel

run.test.ts 6.3 KB

You have to be logged in to leave a comment. Sign In
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  1. import { Command } from 'commander';
  2. import logger from '../../../src/logger';
  3. import { redteamRunCommand } from '../../../src/redteam/commands/run';
  4. import { doRedteamRun } from '../../../src/redteam/shared';
  5. import { getConfigFromCloud } from '../../../src/util/cloud';
  6. jest.mock('../../../src/cliState', () => ({
  7. remote: false,
  8. }));
  9. jest.mock('../../../src/telemetry', () => ({
  10. record: jest.fn(),
  11. }));
  12. jest.mock('../../../src/util', () => ({
  13. setupEnv: jest.fn(),
  14. }));
  15. jest.mock('../../../src/redteam/shared', () => ({
  16. doRedteamRun: jest.fn().mockResolvedValue({}),
  17. }));
  18. jest.mock('../../../src/util/cloud', () => ({
  19. getConfigFromCloud: jest.fn(),
  20. }));
  21. describe('redteamRunCommand', () => {
  22. let program: Command;
  23. let originalExitCode: number | undefined;
  24. beforeEach(() => {
  25. jest.resetAllMocks();
  26. program = new Command();
  27. redteamRunCommand(program);
  28. // Store original exitCode to restore later
  29. originalExitCode = process.exitCode as number | undefined;
  30. process.exitCode = 0;
  31. });
  32. afterEach(() => {
  33. // Restore original exitCode
  34. process.exitCode = originalExitCode;
  35. });
  36. it('should use target option to set cloud target when UUID is provided', async () => {
  37. // Mock the getConfigFromCloud function
  38. const mockConfig = {
  39. prompts: ['Test prompt'],
  40. vars: {},
  41. providers: [{ id: 'test-provider' }],
  42. targets: [
  43. {
  44. id: 'test-provider',
  45. },
  46. ],
  47. };
  48. jest.mocked(getConfigFromCloud).mockResolvedValue(mockConfig);
  49. // UUID format for config and target
  50. const configUUID = '12345678-1234-1234-1234-123456789012';
  51. const targetUUID = '87654321-4321-4321-4321-210987654321';
  52. // Find the run command
  53. const runCommand = program.commands.find((cmd) => cmd.name() === 'run');
  54. expect(runCommand).toBeDefined();
  55. // Execute the command with the target option
  56. await runCommand!.parseAsync(['node', 'test', '--config', configUUID, '--target', targetUUID]);
  57. // Verify doRedteamRun was called with the right parameters
  58. expect(doRedteamRun).toHaveBeenCalledWith(
  59. expect.objectContaining({
  60. liveRedteamConfig: mockConfig,
  61. config: undefined,
  62. loadedFromCloud: true,
  63. target: targetUUID,
  64. }),
  65. );
  66. });
  67. it('should not support target argument with a local config file', async () => {
  68. // Find the run command
  69. const runCommand = program.commands.find((cmd) => cmd.name() === 'run');
  70. expect(runCommand).toBeDefined();
  71. const configPath = 'path/to/config.yaml';
  72. const targetUUID = '87654321-4321-4321-4321-210987654321';
  73. // Execute the command with the target option but a path config
  74. await runCommand!.parseAsync(['node', 'test', '--config', configPath, '--target', targetUUID]);
  75. // Should log error message
  76. expect(logger.error).toHaveBeenCalledWith(
  77. `Target ID (-t) can only be used when -c is used. To use a cloud target inside of a config set the id of the target to promptfoo://provider/${targetUUID}. `,
  78. );
  79. // Should set exit code to 1
  80. expect(process.exitCode).toBe(1);
  81. // getConfigFromCloud should not be called
  82. expect(getConfigFromCloud).not.toHaveBeenCalled();
  83. // doRedteamRun should not be called
  84. expect(doRedteamRun).not.toHaveBeenCalled();
  85. });
  86. it('should not support target argument when no cloud config file is provided', async () => {
  87. // Find the run command
  88. const runCommand = program.commands.find((cmd) => cmd.name() === 'run');
  89. expect(runCommand).toBeDefined();
  90. const targetUUID = '87654321-4321-4321-4321-210987654321';
  91. // Execute the command with the target option but a path config
  92. await runCommand!.parseAsync(['node', 'test', '--target', targetUUID]);
  93. // Should log error message
  94. expect(logger.error).toHaveBeenCalledWith(
  95. `Target ID (-t) can only be used when -c is used. To use a cloud target inside of a config set the id of the target to promptfoo://provider/${targetUUID}. `,
  96. );
  97. // Should set exit code to 1
  98. expect(process.exitCode).toBe(1);
  99. // getConfigFromCloud should not be called
  100. expect(getConfigFromCloud).not.toHaveBeenCalled();
  101. // doRedteamRun should not be called
  102. expect(doRedteamRun).not.toHaveBeenCalled();
  103. });
  104. it('should throw error when target is not a UUID', async () => {
  105. // UUID format for config but not for target
  106. const configUUID = '12345678-1234-1234-1234-123456789012';
  107. const invalidTarget = 'not-a-uuid';
  108. // Find the run command
  109. const runCommand = program.commands.find((cmd) => cmd.name() === 'run');
  110. expect(runCommand).toBeDefined();
  111. // Execute the command with the target option and expect it to throw
  112. await expect(
  113. runCommand!.parseAsync(['node', 'test', '--config', configUUID, '--target', invalidTarget]),
  114. ).rejects.toThrow('Invalid target ID, it must be a valid UUID');
  115. // Verify getConfigFromCloud was not called
  116. expect(getConfigFromCloud).not.toHaveBeenCalled();
  117. });
  118. it('should handle backwards compatibility with empty targets and a valid target UUID', async () => {
  119. // Mock the getConfigFromCloud function to return config without targets
  120. const mockConfig = {
  121. prompts: ['Test prompt'],
  122. vars: {},
  123. providers: [{ id: 'test-provider' }],
  124. targets: [], // Empty targets
  125. };
  126. jest.mocked(getConfigFromCloud).mockResolvedValue(mockConfig);
  127. // UUID format for config and target
  128. const configUUID = '12345678-1234-1234-1234-123456789012';
  129. const targetUUID = '87654321-4321-4321-4321-210987654321';
  130. // Find the run command
  131. const runCommand = program.commands.find((cmd) => cmd.name() === 'run');
  132. expect(runCommand).toBeDefined();
  133. // Execute the command with the target option
  134. await runCommand!.parseAsync(['node', 'test', '--config', configUUID, '--target', targetUUID]);
  135. // Verify that a target was added to the config with the CLOUD_PROVIDER_PREFIX
  136. expect(mockConfig.targets).toEqual([
  137. {
  138. id: `promptfoo://provider/${targetUUID}`,
  139. config: {},
  140. },
  141. ]);
  142. // Verify doRedteamRun was called with the updated config
  143. expect(doRedteamRun).toHaveBeenCalledWith(
  144. expect.objectContaining({
  145. liveRedteamConfig: mockConfig,
  146. config: undefined,
  147. loadedFromCloud: true,
  148. target: targetUUID,
  149. }),
  150. );
  151. });
  152. });
Tip!

Press p or to see the previous file or, n or to see the next file

Comments

Loading...