diff --git a/test/ce/server/test_evil_cases.py b/test/ce/server/test_evil_cases.py index eaafcc00ed..508bfeabd4 100644 --- a/test/ce/server/test_evil_cases.py +++ b/test/ce/server/test_evil_cases.py @@ -122,3 +122,332 @@ def test_stop_seq_exceed_length(): resp = send_request(URL, payload).json() assert resp.get("detail").get("object") == "error", "stop 超出长度应触发异常" assert "exceeds the limit stop_seqs_max_len" in resp.get("detail").get("message", ""), "未返回预期的报错信息" + + +def test_multilingual_input(): + """测试多语言混合输入是否能够被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "这是一个包含多种语言的输入:Hello, 世界!Bonjour, le monde! Hola, el mundo! こんにちは、世界!" + } + ], + "stream": False, + + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + + # 验证响应是否包含有效的回复 + assert "choices" in resp, "未收到有效的回复" + assert len(resp["choices"]) > 0, "回复为空" + assert "message" in resp["choices"][0], "回复中未包含消息内容" + assert "content" in resp["choices"][0]["message"], "回复中未包含内容字段" + # 验证模型是否能够正确处理多语言输入 + response_content = resp["choices"][0]["message"]["content"] + assert response_content.strip() != "", "模型未生成任何内容" + print("多语言混合输入测试通过!") + + + +def test_too_long_input(): + """测试超长输入是否被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "a," * 200000 # 超过最大输入长度 + } + ], + "stream": False + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp["detail"].get("object") == "error", "超长输入未被识别为错误" + assert "Input text is too long" in resp["detail"].get("message", ""), "未检测到最大长度限制错误" + + +def test_empty_input(): + """测试空输入是否被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "" # 空输入 + } + ], + "stream": False + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert "error" not in resp.get("object"), "空输入被识别为错误" + assert len(resp["choices"][0]["message"]["content"]) > 0, "内容为空时,回复为空" + + +def test_prompt_only_spaces(): + """messages content 为纯空格字符串,服务正常返回""" + data = { + "messages": [ + { + "role": "user", + "content": " ", # 纯空格 + } + ], + "stream": False, + "max_tokens": 10, + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + response_content = resp["choices"][0]["message"]["content"] + assert len(response_content) > 0, "messages content为空,未正常生成回复" + + +def test_illegal_characters(): + """测试非法字符输入是否被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?:\x00\x01\x02" # 非法字符 + } + ], + "stream": False + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert len(resp["choices"][0]["message"]["content"]) > 0, "非法字符输入影响模型回复" + + +def test_sql_injection(): + """测试 SQL 注入攻击是否被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "SELECT * FROM users WHERE username = 'admin' OR '1'='1';" # SQL 注入攻击 + } + ], + "stream": False, + "max_tokens": 50, + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert "message" in resp["choices"][0], "回复中未包含消息内容" + assert "content" in resp["choices"][0]["message"], "回复中未包含内容字段" + response_content = resp["choices"][0]["message"]["content"] + assert len(response_content) > 0, "SQL 注入攻击影响模型回复" + + +def test_xss_attack(): + """测试 XSS 攻击是否被正确处理""" + data = { + "messages": [ + { + "role": "user", + "content": "" # XSS 攻击 + } + ], + "stream": False, + "max_tokens": 50, + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert "message" in resp["choices"][0], "回复中未包含消息内容" + assert "content" in resp["choices"][0]["message"], "回复中未包含内容字段" + response_content = resp["choices"][0]["message"]["content"] + assert len(response_content) > 0, "XSS 攻击未被正确处理" + + +def test_stop_empty_string(): + """测试 stop 参数为空字符串时的行为""" + data = { + "stream": False, + "messages": [ + { + "role": "user", + "content": "非洲的首都是?" + } + ], + "max_tokens": 10, + "stop": "" # 空字符串 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + assert len(resp.get("choices", [])[0].get("message", {}).get("content", "")) > 0, "应生成有效文本" + + +def test_stop_multiple_strings(): + """测试 stop 参数为多个字符串时的行为""" + data = { + "stream": False, + "messages": [ + { + "role": "user", + "content": "非洲的首都是?" + } + ], + "max_tokens": 50, + "stop": ["。", "!", "?"] # 多个停止条件 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + generated_text = resp.get("choices")[0].get("message", {}).get("content", "") + assert any(stop in generated_text for stop in data["stop"]), "生成文本应包含 stop 序列之一" + + +def test_stop_with_special_characters(): + """测试 stop 参数为包含特殊字符的字符串时的行为""" + data = { + "stream": False, + "messages": [ + { + "role": "user", + "content": "非洲的首都是?" + } + ], + "max_tokens": 50, + "stop": "!@#$%^&*()" # 包含特殊字符 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + generated_text = resp.get("choices")[0].get("message", {}).get("content", "") + assert any(char in generated_text for char in data["stop"]), "生成文本应包含 stop 序列中的特殊字符之一" + + +def test_stop_with_newlines(): + """测试 stop 参数为包含换行符的字符串时的行为""" + data = { + "stream": False, + "messages": [ + { + "role": "user", + "content": "非洲的首都是?" + } + ], + "max_tokens": 50, + "stop": "\n\n" # 包含换行符 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + generated_text = resp.get("choices")[0].get("message", {}).get("content", "") + assert data["stop"] in generated_text, "生成文本应包含 stop 序列" + + +def test_model_empty(): + """model 参数为空,不影响服务""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": 10, + "model": "" # 空模型 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "应返回 chat.completion 对象" + response_content = resp["choices"][0]["message"]["content"] + assert len(response_content) > 0, "模型名为空,未正常生成回复" + + +def test_model_invalid(): + """model 参数为不存在的模型,不影响服务""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": 10, + "model": "non-existent-model" # 不存在的模型 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "不存在的 model 应触发校验异常" + assert "non-existent-model" in resp.get("model"), "未返回预期的 model 信息" + assert len(resp.get("choices")[0].get("message").get("content")) > 0, "模型名为不存在的 model,未正常生成回复" + + +def test_model_with_special_characters(): + """model 参数为非法格式(例如包含特殊字符),不影响服务""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": 10, + "model": "!@#" # 包含特殊字符 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("object") == "chat.completion", "不存在的 model 应触发校验异常" + assert "!@#" in resp.get("model"), "未返回预期的 model 信息" + assert len(resp.get("choices")[0].get("message").get("content")) > 0, "模型名为model 参数为非法格式,未正常生成回复" + + +def test_max_tokens_negative(): + """max_tokens 为负数,服务应报错""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": -10, # 负数 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get("detail").get("object") == "error", "max_tokens < 0 未触发校验异常" + assert 'max_tokens can be defined [1,' in resp.get("detail").get("message"), "未返回预期的 max_tokens 错误信息" + + +def test_max_tokens_min(): + """测试 max_tokens 达到异常值0 时的行为""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": 0, # 最小值 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get('detail').get("object") == "error", "max_tokens未0时API未拦截住" + assert "reasoning_max_tokens must be between max_tokens and 1" in resp.get('detail').get("message", ""), "未返回预期的 max_tokens 达到异常值0 的 错误信息" + + +def test_max_tokens_non_integer(): + """max_tokens 为非整数,服务应报错""" + data = { + "messages": [ + { + "role": "user", + "content": "非洲的首都是?", + } + ], + "stream": False, + "max_tokens": 10.5, # 非整数 + } + payload = build_request_payload(TEMPLATE, data) + resp = send_request(URL, payload).json() + assert resp.get('detail')[0].get("msg") == "Input should be a valid integer, got a number with a fractional part", "未返回预期的 max_tokens 为非整数的错误信息" +