MockOpenAIApiTest.php 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. namespace Tests\Feature;
  3. use Tests\TestCase;
  4. class MockOpenAIApiTest extends TestCase
  5. {
  6. protected string $baseUrl = '/api/v2/mock/openai';
  7. protected string $validApiKey = 'Bearer test-api-key-12345';
  8. protected string $invalidApiKey = 'Bearer invalid-key';
  9. /**
  10. * 测试聊天完成 API - 成功响应
  11. */
  12. public function test_openai_completions_success_response()
  13. {
  14. $response = $this->postJson($this->baseUrl . '/chat/completions', [
  15. 'model' => 'gpt-3.5-turbo',
  16. 'messages' => [
  17. [
  18. 'role' => 'user',
  19. 'content' => 'Hello, this is a test message'
  20. ]
  21. ]
  22. ], [
  23. 'Authorization' => $this->validApiKey
  24. ]);
  25. // 由于有随机错误,我们需要处理可能的错误响应
  26. if ($response->status() === 200) {
  27. $response->assertStatus(200)
  28. ->assertJsonStructure([
  29. 'id',
  30. 'object',
  31. 'created',
  32. 'model',
  33. 'choices' => [
  34. '*' => [
  35. 'index',
  36. 'message' => [
  37. 'role',
  38. 'content'
  39. ],
  40. 'finish_reason'
  41. ]
  42. ],
  43. 'usage' => [
  44. 'prompt_tokens',
  45. 'completion_tokens',
  46. 'total_tokens'
  47. ]
  48. ]);
  49. $responseData = $response->json();
  50. $this->assertEquals('chat.completion', $responseData['object']);
  51. $this->assertEquals('gpt-3.5-turbo', $responseData['model']);
  52. $this->assertEquals('assistant', $responseData['choices'][0]['message']['role']);
  53. $this->assertStringContainsString('模拟', $responseData['choices'][0]['message']['content']);
  54. } else {
  55. // 如果是错误响应,验证错误格式
  56. $this->assertContains($response->status(), [400, 429, 500]);
  57. $response->assertJsonStructure([
  58. 'error' => [
  59. 'message',
  60. 'type'
  61. ]
  62. ]);
  63. }
  64. }
  65. /**
  66. * 测试文本完成 API - 成功响应
  67. */
  68. public function test_completions_success_response()
  69. {
  70. $response = $this->postJson($this->baseUrl . '/completions', [
  71. 'model' => 'text-davinci-003',
  72. 'prompt' => 'Once upon a time'
  73. ], [
  74. 'Authorization' => $this->validApiKey
  75. ]);
  76. if ($response->status() === 200) {
  77. $response->assertStatus(200)
  78. ->assertJsonStructure([
  79. 'id',
  80. 'object',
  81. 'created',
  82. 'model',
  83. 'choices' => [
  84. '*' => [
  85. 'text',
  86. 'index',
  87. 'logprobs',
  88. 'finish_reason'
  89. ]
  90. ],
  91. 'usage'
  92. ]);
  93. $responseData = $response->json();
  94. $this->assertEquals('text_completion', $responseData['object']);
  95. $this->assertEquals('text-davinci-003', $responseData['model']);
  96. } else {
  97. $this->assertContains($response->status(), [400, 429, 500]);
  98. }
  99. }
  100. /**
  101. * 测试模型列表 API
  102. */
  103. public function test_models_list_response()
  104. {
  105. $response = $this->getJson($this->baseUrl . '/models', [
  106. 'Authorization' => $this->validApiKey
  107. ]);
  108. if ($response->status() === 200) {
  109. $response->assertStatus(200)
  110. ->assertJsonStructure([
  111. 'object',
  112. 'data' => [
  113. '*' => [
  114. 'id',
  115. 'object',
  116. 'created',
  117. 'owned_by'
  118. ]
  119. ]
  120. ]);
  121. $responseData = $response->json();
  122. $this->assertEquals('list', $responseData['object']);
  123. $this->assertGreaterThan(0, count($responseData['data']));
  124. // 验证包含预期的模型
  125. $modelIds = collect($responseData['data'])->pluck('id')->toArray();
  126. $this->assertContains('gpt-4', $modelIds);
  127. $this->assertContains('gpt-3.5-turbo', $modelIds);
  128. } else {
  129. $this->assertContains($response->status(), [400, 429, 500]);
  130. }
  131. }
  132. /**
  133. * 测试错误响应格式
  134. */
  135. public function test_error_response_formats()
  136. {
  137. // 多次请求以增加遇到错误的概率
  138. for ($i = 0; $i < 10; $i++) {
  139. $response = $this->postJson($this->baseUrl . '/chat/completions', [
  140. 'model' => 'gpt-3.5-turbo',
  141. 'messages' => [
  142. ['role' => 'user', 'content' => "Test message $i"]
  143. ]
  144. ], [
  145. 'Authorization' => $this->validApiKey
  146. ]);
  147. if (in_array($response->status(), [400, 429, 500])) {
  148. $response->assertJsonStructure([
  149. 'error' => [
  150. 'message',
  151. 'type'
  152. ]
  153. ]);
  154. $errorData = $response->json()['error'];
  155. $this->assertNotEmpty($errorData['message']);
  156. $this->assertNotEmpty($errorData['type']);
  157. // 验证特定错误类型
  158. switch ($response->status()) {
  159. case 400:
  160. $this->assertEquals('invalid_request_error', $errorData['type']);
  161. break;
  162. case 429:
  163. $this->assertEquals('requests', $errorData['type']);
  164. break;
  165. case 500:
  166. $this->assertEquals('server_error', $errorData['type']);
  167. break;
  168. }
  169. // 找到一个错误响应就足够了
  170. break;
  171. }
  172. }
  173. }
  174. /**
  175. * 测试响应时间(延迟)
  176. */
  177. public function test_response_delay()
  178. {
  179. $startTime = microtime(true);
  180. $response = $this->postJson($this->baseUrl . '/chat/completions', [
  181. 'model' => 'gpt-3.5-turbo',
  182. 'messages' => [
  183. ['role' => 'user', 'content' => 'Test delay']
  184. ]
  185. ], [
  186. 'Authorization' => $this->validApiKey
  187. ]);
  188. $endTime = microtime(true);
  189. $duration = $endTime - $startTime;
  190. // 验证至少有1秒延迟
  191. $this->assertGreaterThanOrEqual(1, $duration);
  192. // 记录响应时间用于调试
  193. echo "\nResponse time: " . number_format($duration, 2) . " seconds\n";
  194. }
  195. /**
  196. * 测试请求参数验证
  197. */
  198. public function test_request_parameters()
  199. {
  200. // 测试自定义模型参数
  201. $response = $this->postJson($this->baseUrl . '/chat/completions', [
  202. 'model' => 'gpt-4',
  203. 'messages' => [
  204. ['role' => 'user', 'content' => 'Hello with GPT-4']
  205. ],
  206. 'max_tokens' => 100,
  207. 'temperature' => 0.7
  208. ], [
  209. 'Authorization' => $this->validApiKey
  210. ]);
  211. if ($response->status() === 200) {
  212. $responseData = $response->json();
  213. $this->assertEquals('gpt-4', $responseData['model']);
  214. }
  215. }
  216. /**
  217. * 测试并发请求
  218. */
  219. public function test_concurrent_requests()
  220. {
  221. $promises = [];
  222. $responses = [];
  223. // 发送5个并发请求
  224. for ($i = 0; $i < 5; $i++) {
  225. $responses[] = $this->postJson($this->baseUrl . '/chat/completions', [
  226. 'model' => 'gpt-3.5-turbo',
  227. 'messages' => [
  228. ['role' => 'user', 'content' => "Concurrent test $i"]
  229. ]
  230. ], [
  231. 'Authorization' => $this->validApiKey
  232. ]);
  233. }
  234. // 验证所有响应
  235. foreach ($responses as $index => $response) {
  236. $this->assertContains($response->status(), [200, 400, 429, 500]);
  237. if ($response->status() === 200) {
  238. $response->assertJsonStructure(['id', 'object', 'choices']);
  239. } else {
  240. $response->assertJsonStructure(['error']);
  241. }
  242. }
  243. }
  244. }