|
1 | 1 | # SPDX-License-Identifier: Apache-2.0
|
| 2 | +from typing import Final |
| 3 | + |
2 | 4 | import pytest
|
3 | 5 | import schemathesis
|
| 6 | +from hypothesis import settings |
4 | 7 | from schemathesis import GenerationConfig
|
5 | 8 |
|
6 | 9 | from ...utils import RemoteOpenAIServer
|
|
9 | 12 |
|
10 | 13 | MODEL_NAME = "HuggingFaceTB/SmolVLM-256M-Instruct"
|
11 | 14 | MAXIMUM_IMAGES = 2
|
| 15 | +DEFAULT_TIMEOUT_SECONDS: Final[int] = 10 |
| 16 | +LONG_TIMEOUT_SECONDS: Final[int] = 60 |
12 | 17 |
|
13 | 18 |
|
14 | 19 | @pytest.fixture(scope="module")
|
@@ -42,8 +47,58 @@ def get_schema(server):
|
42 | 47 | schema = schemathesis.from_pytest_fixture("get_schema")
|
43 | 48 |
|
44 | 49 |
|
| 50 | +@schemathesis.hook |
| 51 | +def before_generate_case(context: schemathesis.hooks.HookContext, strategy): |
| 52 | + op = context.operation |
| 53 | + assert op is not None |
| 54 | + |
| 55 | + def no_file_type(case: schemathesis.models.Case): |
| 56 | + """ |
| 57 | + This filter skips test cases for the `POST /tokenize` endpoint where the |
| 58 | + HTTP request body uses `"type": "file"` in any message's content. |
| 59 | + We expect these cases to fail because that type isn't implemented here |
| 60 | + https://github.com/vllm-project/vllm/blob/0b34593017953051b3225b1483ce0f4670e3eb0e/vllm/entrypoints/chat_utils.py#L1038-L1095 |
| 61 | +
|
| 62 | + Example test cases that are skipped: |
| 63 | + curl -X POST -H 'Content-Type: application/json' \ |
| 64 | + -d '{"messages": [{"role": "assistant"}, {"content": [{"file": {}, "type": "file"}], "role": "user"}]}' \ |
| 65 | + http://localhost:8000/tokenize |
| 66 | +
|
| 67 | + curl -X POST -H 'Content-Type: application/json' \ |
| 68 | + -d '{"messages": [{"content": [{"file": {}, "type": "file"}], "role": "user"}]}' \ |
| 69 | + http://localhost:8000/tokenize |
| 70 | + """ # noqa: E501 |
| 71 | + if (op.method.lower() == "post" and op.path == "/tokenize" |
| 72 | + and hasattr(case, "body") and isinstance(case.body, dict) |
| 73 | + and "messages" in case.body |
| 74 | + and isinstance(case.body["messages"], list) |
| 75 | + and len(case.body["messages"]) > 0): |
| 76 | + for message in case.body["messages"]: |
| 77 | + if not isinstance(message, dict): |
| 78 | + continue |
| 79 | + content = message.get("content", []) |
| 80 | + if not isinstance(content, list) or len(content) == 0: |
| 81 | + continue |
| 82 | + if any(item.get("type") == "file" for item in content): |
| 83 | + return False |
| 84 | + return True |
| 85 | + |
| 86 | + return strategy.filter(no_file_type) |
| 87 | + |
| 88 | + |
45 | 89 | @schema.parametrize()
|
46 | 90 | @schema.override(headers={"Content-Type": "application/json"})
|
| 91 | +@settings(deadline=LONG_TIMEOUT_SECONDS * 1000) |
47 | 92 | def test_openapi_stateless(case: schemathesis.Case):
|
| 93 | + key = ( |
| 94 | + case.operation.method.upper(), |
| 95 | + case.operation.path, |
| 96 | + ) |
| 97 | + timeout = { |
| 98 | + # requires a longer timeout |
| 99 | + ("POST", "/v1/chat/completions"): |
| 100 | + LONG_TIMEOUT_SECONDS, |
| 101 | + }.get(key, DEFAULT_TIMEOUT_SECONDS) |
| 102 | + |
48 | 103 | #No need to verify SSL certificate for localhost
|
49 |
| - case.call_and_validate(verify=False) |
| 104 | + case.call_and_validate(verify=False, timeout=timeout) |
0 commit comments