1 line
31 KiB
JSON
1 line
31 KiB
JSON
{"repo": "abdulwahed-sweden/deepseek-rust", "files": {".env.example": {"1616ebf83d92be64f851259bf3116708881c93cb": {"t": 1785745153.6790433, "n": 910, "h": "b9ab510e9d661f16", "c": "# DeepSeek API Configuration\n# Get your API key from: https://platform.deepseek.com/api_keys\n\n# Required: Your DeepSeek API key sk-ebecb14d27d04108a474a28ea70c9fad\nDEEPSEEK_API_KEY=your_api_key_here\n\n# Optional: API base URL (default: https://api.deepseek.com)\nDEEPSEEK_API_BASE_URL=https://api.deepseek.com\n\n# Optional: Request timeout in seconds (default: 30)\nDEEPSEEK_TIMEOUT_SECONDS=30\n\n# Optional: Maximum number of retries for failed requests (default: 3)\nDEEPSEEK_MAX_RETRIES=3\n\n# Optional: Proxy URL if you need to use a proxy\n# DEEPSEEK_PROXY=http://proxy.example.com:8080\n\n# Optional: Whether to validate SSL certificates (default: true)\n# Set to false only for development/testing\nDEEPSEEK_VALIDATE_CERTS=true\n\n# Logging Configuration (for examples and testing)\n# Options: error, warn, info, debug, trace\nRUST_LOG=deepseek_rust=info\n\n# Development settings\n# RUST_BACKTRACE=1 # Enable for debugging"}, "30b1a857e1be5bb3a33463bd23b14bda67d67e91": {"t": 1785748241.1511788, "n": 910, "h": "b9ab510e9d661f16", "c": "# DeepSeek API Configuration\n# Get your API key from: https://platform.deepseek.com/api_keys\n\n# Required: Your DeepSeek API key sk-ebecb14d27d04108a474a28ea70c9fad\nDEEPSEEK_API_KEY=your_api_key_here\n\n# Optional: API base URL (default: https://api.deepseek.com)\nDEEPSEEK_API_BASE_URL=https://api.deepseek.com\n\n# Optional: Request timeout in seconds (default: 30)\nDEEPSEEK_TIMEOUT_SECONDS=30\n\n# Optional: Maximum number of retries for failed requests (default: 3)\nDEEPSEEK_MAX_RETRIES=3\n\n# Optional: Proxy URL if you need to use a proxy\n# DEEPSEEK_PROXY=http://proxy.example.com:8080\n\n# Optional: Whether to validate SSL certificates (default: true)\n# Set to false only for development/testing\nDEEPSEEK_VALIDATE_CERTS=true\n\n# Logging Configuration (for examples and testing)\n# Options: error, warn, info, debug, trace\nRUST_LOG=deepseek_rust=info\n\n# Development settings\n# RUST_BACKTRACE=1 # Enable for debugging"}}, "tests/integration.rs": {"1616ebf83d92be64f851259bf3116708881c93cb": {"t": 1785745859.9284832, "n": 13781, "h": "2e430331d7e6856f", "c": "//! Integration tests for DeepSeek Rust client\n\nuse deepseek_rust::{\n ChatCompletionRequest, DeepSeekClient, DeepSeekConfig, DeepSeekError, Message, Model,\n Result, Temperature,\n};\nuse mockito::{mock, server_url, Matcher};\nuse serde_json::json;\n\n/// Helper function to create a test client with mock server\nfn create_test_client() -> DeepSeekClient {\n let config = DeepSeekConfig::new(\"test-api-key\")\n .with_base_url(server_url())\n .with_max_retries(1);\n \n DeepSeekClient::new(config).expect(\"Failed to create test client\")\n}\n\n/// Helper function to create a mock successful response\nfn mock_success_response() -> serde_json::Value {\n json!({\n \"id\": \"chatcmpl-123\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"deepseek-chat\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"Hello! How can I help you today?\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 8,\n \"total_tokens\": 18\n }\n })\n}\n\n/// Helper function to create a mock error response\nfn mock_error_response(status: u16, message: &str) -> serde_json::Value {\n json!({\n \"error\": {\n \"message\": message,\n \"type\": \"invalid_request_error\",\n \"code\": \"invalid_api_key\"\n }\n })\n}\n\n#[tokio::test]\nasync fn test_simple_chat_completion() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_header(\"authorization\", \"Bearer test-api-key\")\n .match_header(\"content-type\", \"application/json\")\n .expect(1)\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert_eq!(response.choices.len(), 1);\n assert_eq!(\n response.choices[0].message.content.as_ref().unwrap(),\n \"Hello! How can I help you today?\"\n );\n assert_eq!(response.usage.as_ref().unwrap().total_tokens, 18);\n}\n\n#[tokio::test]\nasync fn test_chat_with_system_message() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"messages\": [\n {\"role\": \"system\", \"content\": \"You are a helpful assistant\"},\n {\"role\": \"user\", \"content\": \"Hello\"}\n ]\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_system_message(\"You are a helpful assistant\")\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_chat_with_parameters() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-coder\",\n \"temperature\": 0.5,\n \"max_tokens\": 100\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Write code\")\n .with_model(Model::Coder)\n .with_temperature(0.5)\n .expect(\"Temperature should be valid\")\n .with_max_tokens(100)\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_reasoning_model_response() {\n let reasoning_response = json!({\n \"id\": \"chatcmpl-reasoning\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"deepseek-reasoner\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"The answer is 42\",\n \"reasoning_content\": \"Let me think step by step...\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 8,\n \"total_tokens\": 18,\n \"reasoning_tokens\": 5\n }\n });\n\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(reasoning_response.to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-reasoner\"\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"What is 6 * 7?\")\n .with_model(Model::Reasoner)\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert_eq!(\n response.choices[0].message.content.as_ref().unwrap(),\n \"The answer is 42\"\n );\n assert_eq!(\n response.choices[0].message.reasoning_content.as_ref().unwrap(),\n \"Let me think step by step...\"\n );\n assert_eq!(response.usage.as_ref().unwrap().reasoning_tokens, Some(5));\n}\n\n#[tokio::test]\nasync fn test_authentication_error() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(401)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_error_response(401, \"Invalid API key\").to_string())\n .create();\n\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await;\n\n assert!(result.is_err());\n match result.unwrap_err() {\n DeepSeekError::InvalidParameter(msg) => {\n assert!(msg.contains(\"At least one message is required\"));\n }\n _ => panic!(\"Expected InvalidParameter error\"),\n }\n}\n\n#[tokio::test]\nasync fn test_invalid_temperature() {\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .with_temperature(3.0); // Invalid: > 2.0\n\n assert!(result.is_err());\n match result.unwrap_err() {\n DeepSeekError::InvalidParameter(msg) => {\n assert!(msg.contains(\"Temperature must be between 0.0 and 2.0\"));\n }\n _ => panic!(\"Expected InvalidParameter error\"),\n }\n}\n\n#[tokio::test]\nasync fn test_multi_turn_conversation() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"messages\": [\n {\"role\": \"user\", \"content\": \"My name is Alice\"},\n {\"role\": \"assistant\", \"content\": \"Nice to meet you, Alice!\"},\n {\"role\": \"user\", \"content\": \"What's my name?\"}\n ]\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"My name is Alice\")\n .add_assistant_message(\"Nice to meet you, Alice!\")\n .add_user_message(\"What's my name?\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_request_with_all_parameters() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-chat\",\n \"temperature\": 0.7,\n \"max_tokens\": 500,\n \"top_p\": 0.9,\n \"frequency_penalty\": 0.5,\n \"presence_penalty\": 0.3,\n \"stop\": [\"END\"],\n \"n\": 2,\n \"user\": \"test-user\"\n })))\n .create();\n\n let request = ChatCompletionRequest::new(vec![Message::user(\"Hello\")])\n .with_model(Model::Chat)\n .with_temperature(Temperature::medium())\n .with_max_tokens(500)\n .with_top_p(0.9)\n .with_frequency_penalty(0.5)\n .with_presence_penalty(0.3)\n .with_stop(vec![\"END\".to_string()])\n .with_n(2)\n .with_user(\"test-user\");\n\n let client = create_test_client();\n let response = client\n .chat_completion(request)\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_retry_on_transient_error() {\n // First request fails with 500, second succeeds\n let _mock_fail = mock(\"POST\", \"/chat/completions\")\n .with_status(500)\n .with_body(\"Internal Server Error\")\n .expect(1)\n .create();\n\n let _mock_success = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .expect(1)\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed after retry\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_connection_test() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .create();\n\n let client = create_test_client();\n let result = client.test_connection().await;\n \n assert!(result.is_ok());\n}\n\n#[tokio::test]\nasync fn test_builder_pattern_chaining() {\n let client = create_test_client();\n \n // Test that all builder methods can be chained\n let builder = client\n .chat()\n .add_system_message(\"System\")\n .add_user_message(\"User\")\n .add_assistant_message(\"Assistant\")\n .with_model(Model::Chat)\n .with_max_tokens(100);\n\n assert_eq!(builder.messages.len(), 3);\n assert_eq!(builder.model, Model::Chat);\n assert_eq!(builder.max_tokens, Some(100));\n}\n\n#[tokio::test]\nasync fn test_response_helpers() {\n let response_json = json!({\n \"id\": \"test\",\n \"object\": \"chat.completion\",\n \"created\": 1234567890,\n \"model\": \"deepseek-chat\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"Test content\",\n \"reasoning_content\": \"Test reasoning\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 5,\n \"total_tokens\": 15\n }\n });\n\n let response: deepseek_rust::ChatCompletionResponse = \n serde_json::from_value(response_json).unwrap();\n\n assert_eq!(response.get_content(), Some(\"Test content\"));\n assert_eq!(response.get_reasoning(), Some(\"Test reasoning\"));\n assert!(response.is_finished());\n assert_eq!(response.total_tokens(), Some(15));\n}\n\n// Test with environment variables\n#[tokio::test]\n#[ignore] // Run with: cargo test -- --ignored\nasync fn test_from_env() {\n // This test requires DEEPSEEK_API_KEY to be set\n let result = DeepSeekClient::from_env();\n \n if std::env::var(\"DEEPSEEK_API_KEY\").is_ok() {\n assert!(result.is_ok());\n let client = result.unwrap();\n \n // Test connection (will make real API call)\n let connection_result = client.test_connection().await;\n assert!(connection_result.is_ok());\n } else {\n assert!(result.is_err());\n }\n}\n\n// Performance test\n#[tokio::test]\nasync fn test_concurrent_requests() {\n use futures::future::join_all;\n \n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .expect_at_least(3)\n .create();\n\n let client = create_test_client();\n \n let futures = vec![\n client.chat().add_user_message(\"Hello 1\").send(),\n client.chat().add_user_message(\"Hello 2\").send(),\n client.chat().add_user_message(\"Hello 3\").send(),\n ];\n \n let results = join_all(futures).await;\n \n for result in results {\n assert!(result.is_ok());\n }\n}());\n let error = result.unwrap_err();\n assert!(error.is_auth_error());\n \n match error {\n DeepSeekError::ApiError { status, message } => {\n assert_eq!(status, 401);\n assert!(message.contains(\"Invalid API key\"));\n }\n _ => panic!(\"Expected ApiError\"),\n }\n}\n\n#[tokio::test]\nasync fn test_rate_limit_error() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(429)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_error_response(429, \"Rate limit exceeded\").to_string())\n .create();\n\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await;\n\n assert!(result.is_err());\n let error = result.unwrap_err();\n assert!(error.is_rate_limit());\n}\n\n#[tokio::test]\nasync fn test_empty_messages_validation() {\n let client = create_test_client();\n let result = client.chat().send().await;\n\n assert!(result.is_err"}, "30b1a857e1be5bb3a33463bd23b14bda67d67e91": {"t": 1785748240.9587708, "n": 13781, "h": "2e430331d7e6856f", "c": "//! Integration tests for DeepSeek Rust client\n\nuse deepseek_rust::{\n ChatCompletionRequest, DeepSeekClient, DeepSeekConfig, DeepSeekError, Message, Model,\n Result, Temperature,\n};\nuse mockito::{mock, server_url, Matcher};\nuse serde_json::json;\n\n/// Helper function to create a test client with mock server\nfn create_test_client() -> DeepSeekClient {\n let config = DeepSeekConfig::new(\"test-api-key\")\n .with_base_url(server_url())\n .with_max_retries(1);\n \n DeepSeekClient::new(config).expect(\"Failed to create test client\")\n}\n\n/// Helper function to create a mock successful response\nfn mock_success_response() -> serde_json::Value {\n json!({\n \"id\": \"chatcmpl-123\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"deepseek-chat\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"Hello! How can I help you today?\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 8,\n \"total_tokens\": 18\n }\n })\n}\n\n/// Helper function to create a mock error response\nfn mock_error_response(status: u16, message: &str) -> serde_json::Value {\n json!({\n \"error\": {\n \"message\": message,\n \"type\": \"invalid_request_error\",\n \"code\": \"invalid_api_key\"\n }\n })\n}\n\n#[tokio::test]\nasync fn test_simple_chat_completion() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_header(\"authorization\", \"Bearer test-api-key\")\n .match_header(\"content-type\", \"application/json\")\n .expect(1)\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert_eq!(response.choices.len(), 1);\n assert_eq!(\n response.choices[0].message.content.as_ref().unwrap(),\n \"Hello! How can I help you today?\"\n );\n assert_eq!(response.usage.as_ref().unwrap().total_tokens, 18);\n}\n\n#[tokio::test]\nasync fn test_chat_with_system_message() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"messages\": [\n {\"role\": \"system\", \"content\": \"You are a helpful assistant\"},\n {\"role\": \"user\", \"content\": \"Hello\"}\n ]\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_system_message(\"You are a helpful assistant\")\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_chat_with_parameters() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-coder\",\n \"temperature\": 0.5,\n \"max_tokens\": 100\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Write code\")\n .with_model(Model::Coder)\n .with_temperature(0.5)\n .expect(\"Temperature should be valid\")\n .with_max_tokens(100)\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_reasoning_model_response() {\n let reasoning_response = json!({\n \"id\": \"chatcmpl-reasoning\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"deepseek-reasoner\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"The answer is 42\",\n \"reasoning_content\": \"Let me think step by step...\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 8,\n \"total_tokens\": 18,\n \"reasoning_tokens\": 5\n }\n });\n\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(reasoning_response.to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-reasoner\"\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"What is 6 * 7?\")\n .with_model(Model::Reasoner)\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert_eq!(\n response.choices[0].message.content.as_ref().unwrap(),\n \"The answer is 42\"\n );\n assert_eq!(\n response.choices[0].message.reasoning_content.as_ref().unwrap(),\n \"Let me think step by step...\"\n );\n assert_eq!(response.usage.as_ref().unwrap().reasoning_tokens, Some(5));\n}\n\n#[tokio::test]\nasync fn test_authentication_error() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(401)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_error_response(401, \"Invalid API key\").to_string())\n .create();\n\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await;\n\n assert!(result.is_err());\n match result.unwrap_err() {\n DeepSeekError::InvalidParameter(msg) => {\n assert!(msg.contains(\"At least one message is required\"));\n }\n _ => panic!(\"Expected InvalidParameter error\"),\n }\n}\n\n#[tokio::test]\nasync fn test_invalid_temperature() {\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .with_temperature(3.0); // Invalid: > 2.0\n\n assert!(result.is_err());\n match result.unwrap_err() {\n DeepSeekError::InvalidParameter(msg) => {\n assert!(msg.contains(\"Temperature must be between 0.0 and 2.0\"));\n }\n _ => panic!(\"Expected InvalidParameter error\"),\n }\n}\n\n#[tokio::test]\nasync fn test_multi_turn_conversation() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"messages\": [\n {\"role\": \"user\", \"content\": \"My name is Alice\"},\n {\"role\": \"assistant\", \"content\": \"Nice to meet you, Alice!\"},\n {\"role\": \"user\", \"content\": \"What's my name?\"}\n ]\n })))\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"My name is Alice\")\n .add_assistant_message(\"Nice to meet you, Alice!\")\n .add_user_message(\"What's my name?\")\n .send()\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_request_with_all_parameters() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .match_body(Matcher::PartialJson(json!({\n \"model\": \"deepseek-chat\",\n \"temperature\": 0.7,\n \"max_tokens\": 500,\n \"top_p\": 0.9,\n \"frequency_penalty\": 0.5,\n \"presence_penalty\": 0.3,\n \"stop\": [\"END\"],\n \"n\": 2,\n \"user\": \"test-user\"\n })))\n .create();\n\n let request = ChatCompletionRequest::new(vec![Message::user(\"Hello\")])\n .with_model(Model::Chat)\n .with_temperature(Temperature::medium())\n .with_max_tokens(500)\n .with_top_p(0.9)\n .with_frequency_penalty(0.5)\n .with_presence_penalty(0.3)\n .with_stop(vec![\"END\".to_string()])\n .with_n(2)\n .with_user(\"test-user\");\n\n let client = create_test_client();\n let response = client\n .chat_completion(request)\n .await\n .expect(\"Request should succeed\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_retry_on_transient_error() {\n // First request fails with 500, second succeeds\n let _mock_fail = mock(\"POST\", \"/chat/completions\")\n .with_status(500)\n .with_body(\"Internal Server Error\")\n .expect(1)\n .create();\n\n let _mock_success = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .expect(1)\n .create();\n\n let client = create_test_client();\n let response = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await\n .expect(\"Request should succeed after retry\");\n\n assert!(response.choices[0].message.content.is_some());\n}\n\n#[tokio::test]\nasync fn test_connection_test() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .create();\n\n let client = create_test_client();\n let result = client.test_connection().await;\n \n assert!(result.is_ok());\n}\n\n#[tokio::test]\nasync fn test_builder_pattern_chaining() {\n let client = create_test_client();\n \n // Test that all builder methods can be chained\n let builder = client\n .chat()\n .add_system_message(\"System\")\n .add_user_message(\"User\")\n .add_assistant_message(\"Assistant\")\n .with_model(Model::Chat)\n .with_max_tokens(100);\n\n assert_eq!(builder.messages.len(), 3);\n assert_eq!(builder.model, Model::Chat);\n assert_eq!(builder.max_tokens, Some(100));\n}\n\n#[tokio::test]\nasync fn test_response_helpers() {\n let response_json = json!({\n \"id\": \"test\",\n \"object\": \"chat.completion\",\n \"created\": 1234567890,\n \"model\": \"deepseek-chat\",\n \"choices\": [{\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"Test content\",\n \"reasoning_content\": \"Test reasoning\"\n },\n \"finish_reason\": \"stop\"\n }],\n \"usage\": {\n \"prompt_tokens\": 10,\n \"completion_tokens\": 5,\n \"total_tokens\": 15\n }\n });\n\n let response: deepseek_rust::ChatCompletionResponse = \n serde_json::from_value(response_json).unwrap();\n\n assert_eq!(response.get_content(), Some(\"Test content\"));\n assert_eq!(response.get_reasoning(), Some(\"Test reasoning\"));\n assert!(response.is_finished());\n assert_eq!(response.total_tokens(), Some(15));\n}\n\n// Test with environment variables\n#[tokio::test]\n#[ignore] // Run with: cargo test -- --ignored\nasync fn test_from_env() {\n // This test requires DEEPSEEK_API_KEY to be set\n let result = DeepSeekClient::from_env();\n \n if std::env::var(\"DEEPSEEK_API_KEY\").is_ok() {\n assert!(result.is_ok());\n let client = result.unwrap();\n \n // Test connection (will make real API call)\n let connection_result = client.test_connection().await;\n assert!(connection_result.is_ok());\n } else {\n assert!(result.is_err());\n }\n}\n\n// Performance test\n#[tokio::test]\nasync fn test_concurrent_requests() {\n use futures::future::join_all;\n \n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(200)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_success_response().to_string())\n .expect_at_least(3)\n .create();\n\n let client = create_test_client();\n \n let futures = vec![\n client.chat().add_user_message(\"Hello 1\").send(),\n client.chat().add_user_message(\"Hello 2\").send(),\n client.chat().add_user_message(\"Hello 3\").send(),\n ];\n \n let results = join_all(futures).await;\n \n for result in results {\n assert!(result.is_ok());\n }\n}());\n let error = result.unwrap_err();\n assert!(error.is_auth_error());\n \n match error {\n DeepSeekError::ApiError { status, message } => {\n assert_eq!(status, 401);\n assert!(message.contains(\"Invalid API key\"));\n }\n _ => panic!(\"Expected ApiError\"),\n }\n}\n\n#[tokio::test]\nasync fn test_rate_limit_error() {\n let _mock = mock(\"POST\", \"/chat/completions\")\n .with_status(429)\n .with_header(\"content-type\", \"application/json\")\n .with_body(mock_error_response(429, \"Rate limit exceeded\").to_string())\n .create();\n\n let client = create_test_client();\n let result = client\n .chat()\n .add_user_message(\"Hello\")\n .send()\n .await;\n\n assert!(result.is_err());\n let error = result.unwrap_err();\n assert!(error.is_rate_limit());\n}\n\n#[tokio::test]\nasync fn test_empty_messages_validation() {\n let client = create_test_client();\n let result = client.chat().send().await;\n\n assert!(result.is_err"}}}} |