Change back to codecov (#2363)
This commit is contained in:
parent
101151b419
commit
8b0eaa097c
|
@ -1,28 +0,0 @@
|
||||||
exclude_patterns:
|
|
||||||
- "sanic/__main__.py"
|
|
||||||
- "sanic/application/logo.py"
|
|
||||||
- "sanic/application/motd.py"
|
|
||||||
- "sanic/reloader_helpers.py"
|
|
||||||
- "sanic/simple.py"
|
|
||||||
- "sanic/utils.py"
|
|
||||||
- ".github/"
|
|
||||||
- "changelogs/"
|
|
||||||
- "docker/"
|
|
||||||
- "docs/"
|
|
||||||
- "examples/"
|
|
||||||
- "scripts/"
|
|
||||||
- "tests/"
|
|
||||||
checks:
|
|
||||||
argument-count:
|
|
||||||
enabled: false
|
|
||||||
file-lines:
|
|
||||||
config:
|
|
||||||
threshold: 1000
|
|
||||||
method-count:
|
|
||||||
config:
|
|
||||||
threshold: 40
|
|
||||||
complex-logic:
|
|
||||||
enabled: false
|
|
||||||
method-complexity:
|
|
||||||
config:
|
|
||||||
threshold: 10
|
|
14
.coveragerc
14
.coveragerc
|
@ -3,13 +3,12 @@ branch = True
|
||||||
source = sanic
|
source = sanic
|
||||||
omit =
|
omit =
|
||||||
site-packages
|
site-packages
|
||||||
sanic/application/logo.py
|
|
||||||
sanic/application/motd.py
|
|
||||||
sanic/cli
|
|
||||||
sanic/__main__.py
|
sanic/__main__.py
|
||||||
|
sanic/compat.py
|
||||||
sanic/reloader_helpers.py
|
sanic/reloader_helpers.py
|
||||||
sanic/simple.py
|
sanic/simple.py
|
||||||
sanic/utils.py
|
sanic/utils.py
|
||||||
|
sanic/cli
|
||||||
|
|
||||||
[html]
|
[html]
|
||||||
directory = coverage
|
directory = coverage
|
||||||
|
@ -21,3 +20,12 @@ exclude_lines =
|
||||||
noqa
|
noqa
|
||||||
NOQA
|
NOQA
|
||||||
pragma: no cover
|
pragma: no cover
|
||||||
|
omit =
|
||||||
|
site-packages
|
||||||
|
sanic/__main__.py
|
||||||
|
sanic/compat.py
|
||||||
|
sanic/reloader_helpers.py
|
||||||
|
sanic/simple.py
|
||||||
|
sanic/utils.py
|
||||||
|
sanic/cli
|
||||||
|
skip_empty = True
|
||||||
|
|
11
.github/workflows/coverage.yml
vendored
11
.github/workflows/coverage.yml
vendored
|
@ -20,7 +20,6 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- uses: actions/setup-python@v1
|
- uses: actions/setup-python@v1
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python-version }}
|
python-version: ${{ matrix.python-version }}
|
||||||
|
@ -29,9 +28,9 @@ jobs:
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install tox
|
pip install tox
|
||||||
- uses: paambaati/codeclimate-action@v2.5.3
|
- name: Run coverage
|
||||||
if: always()
|
run: tox -e coverage
|
||||||
env:
|
- uses: codecov/codecov-action@v2
|
||||||
CC_TEST_REPORTER_ID: ${{ secrets.CODECLIMATE }}
|
|
||||||
with:
|
with:
|
||||||
coverageCommand: tox -e coverage
|
files: ./coverage.xml
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
27
codecov.yml
Normal file
27
codecov.yml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
coverage:
|
||||||
|
status:
|
||||||
|
patch:
|
||||||
|
default:
|
||||||
|
target: auto
|
||||||
|
threshold: 0.75
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: auto
|
||||||
|
threshold: 0.5
|
||||||
|
precision: 3
|
||||||
|
codecov:
|
||||||
|
require_ci_to_pass: false
|
||||||
|
ignore:
|
||||||
|
- "sanic/__main__.py"
|
||||||
|
- "sanic/compat.py"
|
||||||
|
- "sanic/reloader_helpers.py"
|
||||||
|
- "sanic/simple.py"
|
||||||
|
- "sanic/utils.py"
|
||||||
|
- "sanic/cli"
|
||||||
|
- ".github/"
|
||||||
|
- "changelogs/"
|
||||||
|
- "docker/"
|
||||||
|
- "docs/"
|
||||||
|
- "examples/"
|
||||||
|
- "scripts/"
|
||||||
|
- "tests/"
|
16
sanic/app.py
16
sanic/app.py
|
@ -114,7 +114,7 @@ if TYPE_CHECKING: # no cov
|
||||||
Extend = TypeVar("Extend") # type: ignore
|
Extend = TypeVar("Extend") # type: ignore
|
||||||
|
|
||||||
|
|
||||||
if OS_IS_WINDOWS:
|
if OS_IS_WINDOWS: # no cov
|
||||||
enable_windows_color_support()
|
enable_windows_color_support()
|
||||||
|
|
||||||
filterwarnings("once", category=DeprecationWarning)
|
filterwarnings("once", category=DeprecationWarning)
|
||||||
|
@ -1554,7 +1554,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
) -> Task:
|
) -> Task:
|
||||||
if not isinstance(task, Future):
|
if not isinstance(task, Future):
|
||||||
prepped = cls._prep_task(task, app, loop)
|
prepped = cls._prep_task(task, app, loop)
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8): # no cov
|
||||||
if name:
|
if name:
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"Cannot set a name for a task when using Python 3.7. "
|
"Cannot set a name for a task when using Python 3.7. "
|
||||||
|
@ -1598,7 +1598,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
|
|
||||||
:param task: future, couroutine or awaitable
|
:param task: future, couroutine or awaitable
|
||||||
"""
|
"""
|
||||||
if name and sys.version_info == (3, 7):
|
if name and sys.version_info < (3, 8): # no cov
|
||||||
name = None
|
name = None
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"Cannot set a name for a task when using Python 3.7. Your "
|
"Cannot set a name for a task when using Python 3.7. Your "
|
||||||
|
@ -1626,7 +1626,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
def get_task(
|
def get_task(
|
||||||
self, name: str, *, raise_exception: bool = True
|
self, name: str, *, raise_exception: bool = True
|
||||||
) -> Optional[Task]:
|
) -> Optional[Task]:
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8): # no cov
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"This feature (get_task) is only supported on using "
|
"This feature (get_task) is only supported on using "
|
||||||
"Python 3.8+."
|
"Python 3.8+."
|
||||||
|
@ -1648,7 +1648,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
*,
|
*,
|
||||||
raise_exception: bool = True,
|
raise_exception: bool = True,
|
||||||
) -> None:
|
) -> None:
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8): # no cov
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"This feature (cancel_task) is only supported on using "
|
"This feature (cancel_task) is only supported on using "
|
||||||
"Python 3.8+."
|
"Python 3.8+."
|
||||||
|
@ -1660,7 +1660,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
if msg:
|
if msg:
|
||||||
if sys.version_info >= (3, 9):
|
if sys.version_info >= (3, 9):
|
||||||
args = (msg,)
|
args = (msg,)
|
||||||
else:
|
else: # no cov
|
||||||
raise RuntimeError(
|
raise RuntimeError(
|
||||||
"Cancelling a task with a message is only supported "
|
"Cancelling a task with a message is only supported "
|
||||||
"on Python 3.9+."
|
"on Python 3.9+."
|
||||||
|
@ -1672,7 +1672,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
...
|
...
|
||||||
|
|
||||||
def purge_tasks(self):
|
def purge_tasks(self):
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8): # no cov
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"This feature (purge_tasks) is only supported on using "
|
"This feature (purge_tasks) is only supported on using "
|
||||||
"Python 3.8+."
|
"Python 3.8+."
|
||||||
|
@ -1709,7 +1709,7 @@ class Sanic(BaseSanic, metaclass=TouchUpMeta):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
if sys.version_info < (3, 8):
|
if sys.version_info < (3, 8): # no cov
|
||||||
error_logger.warning(
|
error_logger.warning(
|
||||||
"This feature (tasks) is only supported on using "
|
"This feature (tasks) is only supported on using "
|
||||||
"Python 3.8+."
|
"Python 3.8+."
|
||||||
|
|
|
@ -53,14 +53,14 @@ class ErrorHandler:
|
||||||
self._warn_fallback_deprecation()
|
self._warn_fallback_deprecation()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fallback(self):
|
def fallback(self): # no cov
|
||||||
# This is for backwards compat and can be removed in v22.6
|
# This is for backwards compat and can be removed in v22.6
|
||||||
if self._fallback is _default:
|
if self._fallback is _default:
|
||||||
return DEFAULT_FORMAT
|
return DEFAULT_FORMAT
|
||||||
return self._fallback
|
return self._fallback
|
||||||
|
|
||||||
@fallback.setter
|
@fallback.setter
|
||||||
def fallback(self, value: str):
|
def fallback(self, value: str): # no cov
|
||||||
self._warn_fallback_deprecation()
|
self._warn_fallback_deprecation()
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise SanicException(
|
raise SanicException(
|
||||||
|
@ -236,7 +236,7 @@ class ErrorHandler:
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
try:
|
||||||
url = repr(request.url)
|
url = repr(request.url)
|
||||||
except AttributeError:
|
except AttributeError: # no cov
|
||||||
url = "unknown"
|
url = "unknown"
|
||||||
response_message = (
|
response_message = (
|
||||||
"Exception raised in exception handler " '"%s" for uri: %s'
|
"Exception raised in exception handler " '"%s" for uri: %s'
|
||||||
|
@ -281,7 +281,7 @@ class ErrorHandler:
|
||||||
if quiet is False or noisy is True:
|
if quiet is False or noisy is True:
|
||||||
try:
|
try:
|
||||||
url = repr(request.url)
|
url = repr(request.url)
|
||||||
except AttributeError:
|
except AttributeError: # no cov
|
||||||
url = "unknown"
|
url = "unknown"
|
||||||
|
|
||||||
error_logger.exception(
|
error_logger.exception(
|
||||||
|
|
12
sanic/log.py
12
sanic/log.py
|
@ -6,7 +6,7 @@ from typing import Any, Dict
|
||||||
from warnings import warn
|
from warnings import warn
|
||||||
|
|
||||||
|
|
||||||
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(
|
LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict( # no cov
|
||||||
version=1,
|
version=1,
|
||||||
disable_existing_loggers=False,
|
disable_existing_loggers=False,
|
||||||
loggers={
|
loggers={
|
||||||
|
@ -57,7 +57,7 @@ LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class Colors(str, Enum):
|
class Colors(str, Enum): # no cov
|
||||||
END = "\033[0m"
|
END = "\033[0m"
|
||||||
BLUE = "\033[01;34m"
|
BLUE = "\033[01;34m"
|
||||||
GREEN = "\033[01;32m"
|
GREEN = "\033[01;32m"
|
||||||
|
@ -65,23 +65,23 @@ class Colors(str, Enum):
|
||||||
RED = "\033[01;31m"
|
RED = "\033[01;31m"
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("sanic.root")
|
logger = logging.getLogger("sanic.root") # no cov
|
||||||
"""
|
"""
|
||||||
General Sanic logger
|
General Sanic logger
|
||||||
"""
|
"""
|
||||||
|
|
||||||
error_logger = logging.getLogger("sanic.error")
|
error_logger = logging.getLogger("sanic.error") # no cov
|
||||||
"""
|
"""
|
||||||
Logger used by Sanic for error logging
|
Logger used by Sanic for error logging
|
||||||
"""
|
"""
|
||||||
|
|
||||||
access_logger = logging.getLogger("sanic.access")
|
access_logger = logging.getLogger("sanic.access") # no cov
|
||||||
"""
|
"""
|
||||||
Logger used by Sanic for access logging
|
Logger used by Sanic for access logging
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
def deprecation(message: str, version: float):
|
def deprecation(message: str, version: float): # no cov
|
||||||
version_info = f"[DEPRECATION v{version}] "
|
version_info = f"[DEPRECATION v{version}] "
|
||||||
if sys.stdout.isatty():
|
if sys.stdout.isatty():
|
||||||
version_info = f"{Colors.RED}{version_info}"
|
version_info = f"{Colors.RED}{version_info}"
|
||||||
|
|
|
@ -13,7 +13,7 @@ ASGISend = Callable[[ASGIMessage], Awaitable[None]]
|
||||||
ASGIReceive = Callable[[], Awaitable[ASGIMessage]]
|
ASGIReceive = Callable[[], Awaitable[ASGIMessage]]
|
||||||
|
|
||||||
|
|
||||||
class MockProtocol:
|
class MockProtocol: # no cov
|
||||||
def __init__(self, transport: "MockTransport", loop):
|
def __init__(self, transport: "MockTransport", loop):
|
||||||
# This should be refactored when < 3.8 support is dropped
|
# This should be refactored when < 3.8 support is dropped
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
|
@ -56,7 +56,7 @@ class MockProtocol:
|
||||||
await self._not_paused.wait()
|
await self._not_paused.wait()
|
||||||
|
|
||||||
|
|
||||||
class MockTransport:
|
class MockTransport: # no cov
|
||||||
_protocol: Optional[MockProtocol]
|
_protocol: Optional[MockProtocol]
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
|
@ -9,7 +9,7 @@ from websockets.typing import Data
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING: # no cov
|
||||||
from .impl import WebsocketImplProtocol
|
from .impl import WebsocketImplProtocol
|
||||||
|
|
||||||
UTF8Decoder = codecs.getincrementaldecoder("utf-8")
|
UTF8Decoder = codecs.getincrementaldecoder("utf-8")
|
||||||
|
@ -37,7 +37,7 @@ class WebsocketFrameAssembler:
|
||||||
"get_id",
|
"get_id",
|
||||||
"put_id",
|
"put_id",
|
||||||
)
|
)
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING: # no cov
|
||||||
protocol: "WebsocketImplProtocol"
|
protocol: "WebsocketImplProtocol"
|
||||||
read_mutex: asyncio.Lock
|
read_mutex: asyncio.Lock
|
||||||
write_mutex: asyncio.Lock
|
write_mutex: asyncio.Lock
|
||||||
|
@ -131,7 +131,7 @@ class WebsocketFrameAssembler:
|
||||||
if self.paused:
|
if self.paused:
|
||||||
self.protocol.resume_frames()
|
self.protocol.resume_frames()
|
||||||
self.paused = False
|
self.paused = False
|
||||||
if not self.get_in_progress:
|
if not self.get_in_progress: # no cov
|
||||||
# This should be guarded against with the read_mutex,
|
# This should be guarded against with the read_mutex,
|
||||||
# exception is here as a failsafe
|
# exception is here as a failsafe
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
|
@ -204,7 +204,7 @@ class WebsocketFrameAssembler:
|
||||||
if self.paused:
|
if self.paused:
|
||||||
self.protocol.resume_frames()
|
self.protocol.resume_frames()
|
||||||
self.paused = False
|
self.paused = False
|
||||||
if not self.get_in_progress:
|
if not self.get_in_progress: # no cov
|
||||||
# This should be guarded against with the read_mutex,
|
# This should be guarded against with the read_mutex,
|
||||||
# exception is here as a failsafe
|
# exception is here as a failsafe
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
|
@ -212,7 +212,7 @@ class WebsocketFrameAssembler:
|
||||||
"asynchronous get was in progress."
|
"asynchronous get was in progress."
|
||||||
)
|
)
|
||||||
self.get_in_progress = False
|
self.get_in_progress = False
|
||||||
if not self.message_complete.is_set():
|
if not self.message_complete.is_set(): # no cov
|
||||||
# This should be guarded against with the read_mutex,
|
# This should be guarded against with the read_mutex,
|
||||||
# exception is here as a failsafe
|
# exception is here as a failsafe
|
||||||
raise ServerError(
|
raise ServerError(
|
||||||
|
@ -220,7 +220,7 @@ class WebsocketFrameAssembler:
|
||||||
"message was complete."
|
"message was complete."
|
||||||
)
|
)
|
||||||
self.message_complete.clear()
|
self.message_complete.clear()
|
||||||
if self.message_fetched.is_set():
|
if self.message_fetched.is_set(): # no cov
|
||||||
# This should be guarded against with the read_mutex,
|
# This should be guarded against with the read_mutex,
|
||||||
# and get_in_progress check, this exception is
|
# and get_in_progress check, this exception is
|
||||||
# here as a failsafe
|
# here as a failsafe
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
import base64
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
|
@ -205,7 +204,3 @@ def sanic_ext(ext_instance): # noqa
|
||||||
yield sanic_ext
|
yield sanic_ext
|
||||||
with suppress(KeyError):
|
with suppress(KeyError):
|
||||||
del sys.modules["sanic_ext"]
|
del sys.modules["sanic_ext"]
|
||||||
|
|
||||||
|
|
||||||
def encode_basic_auth_credentials(username, password):
|
|
||||||
return base64.b64encode(f"{username}:{password}".encode()).decode("ascii")
|
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import base64
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from json import dumps as json_dumps
|
from json import dumps as json_dumps
|
||||||
|
@ -18,7 +19,10 @@ from sanic import Blueprint, Sanic
|
||||||
from sanic.exceptions import ServerError
|
from sanic.exceptions import ServerError
|
||||||
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, RequestParameters
|
from sanic.request import DEFAULT_HTTP_CONTENT_TYPE, RequestParameters
|
||||||
from sanic.response import html, json, text
|
from sanic.response import html, json, text
|
||||||
from tests.conftest import encode_basic_auth_credentials
|
|
||||||
|
|
||||||
|
def encode_basic_auth_credentials(username, password):
|
||||||
|
return base64.b64encode(f"{username}:{password}".encode()).decode("ascii")
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------ #
|
# ------------------------------------------------------------ #
|
||||||
|
|
237
tests/test_websockets.py
Normal file
237
tests/test_websockets.py
Normal file
|
@ -0,0 +1,237 @@
|
||||||
|
import re
|
||||||
|
|
||||||
|
from asyncio import Event, Queue, TimeoutError
|
||||||
|
from unittest.mock import AsyncMock, Mock, call
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from websockets.frames import CTRL_OPCODES, DATA_OPCODES, Frame
|
||||||
|
|
||||||
|
from sanic.exceptions import ServerError
|
||||||
|
from sanic.server.websockets.frame import WebsocketFrameAssembler
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message_incomplete_timeout_0():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete = AsyncMock(spec=Event)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=False)
|
||||||
|
data = await assembler.get(0)
|
||||||
|
|
||||||
|
assert data is None
|
||||||
|
assembler.message_complete.is_set.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message_in_progress():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.get_in_progress = True
|
||||||
|
|
||||||
|
message = re.escape(
|
||||||
|
"Called get() on Websocket frame assembler "
|
||||||
|
"while asynchronous get is already in progress."
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ServerError, match=message):
|
||||||
|
await assembler.get()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message_incomplete():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.wait = AsyncMock(return_value=True)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=False)
|
||||||
|
data = await assembler.get()
|
||||||
|
|
||||||
|
assert data is None
|
||||||
|
assembler.message_complete.wait.assert_awaited_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.wait = AsyncMock(return_value=True)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=True)
|
||||||
|
data = await assembler.get()
|
||||||
|
|
||||||
|
assert data == b""
|
||||||
|
assembler.message_complete.wait.assert_awaited_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message_with_timeout():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.wait = AsyncMock(return_value=True)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=True)
|
||||||
|
data = await assembler.get(0.1)
|
||||||
|
|
||||||
|
assert data == b""
|
||||||
|
assembler.message_complete.wait.assert_awaited_once()
|
||||||
|
assert assembler.message_complete.is_set.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_message_with_timeouterror():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.wait = AsyncMock(return_value=True)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=True)
|
||||||
|
assembler.message_complete.wait.side_effect = TimeoutError("...")
|
||||||
|
data = await assembler.get(0.1)
|
||||||
|
|
||||||
|
assert data == b""
|
||||||
|
assembler.message_complete.wait.assert_awaited_once()
|
||||||
|
assert assembler.message_complete.is_set.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_not_completed():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete = AsyncMock(spec=Event)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=False)
|
||||||
|
data = await assembler.get()
|
||||||
|
|
||||||
|
assert data is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_not_completed_start():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete = AsyncMock(spec=Event)
|
||||||
|
assembler.message_complete.is_set = Mock(side_effect=[False, True])
|
||||||
|
data = await assembler.get(0.1)
|
||||||
|
|
||||||
|
assert data is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_paused():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete = AsyncMock(spec=Event)
|
||||||
|
assembler.message_complete.is_set = Mock(side_effect=[False, True])
|
||||||
|
assembler.paused = True
|
||||||
|
data = await assembler.get()
|
||||||
|
|
||||||
|
assert data is None
|
||||||
|
assembler.protocol.resume_frames.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_data():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete = AsyncMock(spec=Event)
|
||||||
|
assembler.message_complete.is_set = Mock(return_value=True)
|
||||||
|
assembler.chunks = [b"foo", b"bar"]
|
||||||
|
data = await assembler.get()
|
||||||
|
|
||||||
|
assert data == b"foobar"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_iter_in_progress():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.get_in_progress = True
|
||||||
|
|
||||||
|
message = re.escape(
|
||||||
|
"Called get_iter on Websocket frame assembler "
|
||||||
|
"while asynchronous get is already in progress."
|
||||||
|
)
|
||||||
|
|
||||||
|
with pytest.raises(ServerError, match=message):
|
||||||
|
[x async for x in assembler.get_iter()]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_iter_none_in_queue():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.set()
|
||||||
|
assembler.chunks = [b"foo", b"bar"]
|
||||||
|
|
||||||
|
chunks = [x async for x in assembler.get_iter()]
|
||||||
|
|
||||||
|
assert chunks == [b"foo", b"bar"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_ws_frame_get_iter_paused():
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.set()
|
||||||
|
assembler.paused = True
|
||||||
|
|
||||||
|
[x async for x in assembler.get_iter()]
|
||||||
|
assembler.protocol.resume_frames.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", DATA_OPCODES)
|
||||||
|
async def test_ws_frame_put_not_fetched(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_fetched.set()
|
||||||
|
|
||||||
|
message = re.escape(
|
||||||
|
"Websocket put() got a new message when the previous message was "
|
||||||
|
"not yet fetched."
|
||||||
|
)
|
||||||
|
with pytest.raises(ServerError, match=message):
|
||||||
|
await assembler.put(Frame(opcode, b""))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", DATA_OPCODES)
|
||||||
|
async def test_ws_frame_put_fetched(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_fetched = AsyncMock()
|
||||||
|
assembler.message_fetched.is_set = Mock(return_value=False)
|
||||||
|
|
||||||
|
await assembler.put(Frame(opcode, b""))
|
||||||
|
assembler.message_fetched.wait.assert_awaited_once()
|
||||||
|
assembler.message_fetched.clear.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", DATA_OPCODES)
|
||||||
|
async def test_ws_frame_put_message_complete(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.message_complete.set()
|
||||||
|
|
||||||
|
message = re.escape(
|
||||||
|
"Websocket put() got a new message when a message was "
|
||||||
|
"already in its chamber."
|
||||||
|
)
|
||||||
|
with pytest.raises(ServerError, match=message):
|
||||||
|
await assembler.put(Frame(opcode, b""))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", DATA_OPCODES)
|
||||||
|
async def test_ws_frame_put_message_into_queue(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
assembler.chunks_queue = AsyncMock(spec=Queue)
|
||||||
|
assembler.message_fetched = AsyncMock()
|
||||||
|
assembler.message_fetched.is_set = Mock(return_value=False)
|
||||||
|
|
||||||
|
await assembler.put(Frame(opcode, b"foo"))
|
||||||
|
|
||||||
|
assembler.chunks_queue.put.has_calls(
|
||||||
|
call(b"foo"),
|
||||||
|
call(None),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", DATA_OPCODES)
|
||||||
|
async def test_ws_frame_put_not_fin(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
|
||||||
|
retval = await assembler.put(Frame(opcode, b"foo", fin=False))
|
||||||
|
|
||||||
|
assert retval is None
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
@pytest.mark.parametrize("opcode", CTRL_OPCODES)
|
||||||
|
async def test_ws_frame_put_skip_ctrl(opcode):
|
||||||
|
assembler = WebsocketFrameAssembler(Mock())
|
||||||
|
|
||||||
|
retval = await assembler.put(Frame(opcode, b""))
|
||||||
|
|
||||||
|
assert retval is None
|
Loading…
Reference in New Issue
Block a user